From 294317b35b6db0ae59a01e8428c1e2a1f2a7520c Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 3 Apr 2024 21:06:52 +0200 Subject: [PATCH 01/74] minor gradle fixes --- flutter/example/android/app/build.gradle | 2 -- 1 file changed, 2 deletions(-) diff --git a/flutter/example/android/app/build.gradle b/flutter/example/android/app/build.gradle index ac4557e2cd..fc53460b79 100644 --- a/flutter/example/android/app/build.gradle +++ b/flutter/example/android/app/build.gradle @@ -65,8 +65,6 @@ android { } } - // TODO: we need to fix CI as the version 21.1 (default) is not installed by default on - // GH Actions. ndkVersion "25.1.8937393" externalNativeBuild { From 861ebab5fe08bbef9296292cba876af7e8abd543 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 3 Apr 2024 21:49:17 +0200 Subject: [PATCH 02/74] tmp: local sentry-java build --- flutter/android/build.gradle | 3 ++- flutter/example/android/app/build.gradle | 2 +- flutter/example/android/build.gradle | 2 +- flutter/example/android/settings.gradle | 8 ++++++++ min_version_test/android/build.gradle | 2 +- 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/flutter/android/build.gradle b/flutter/android/build.gradle index a2c0f7316e..c13508faaa 100644 --- a/flutter/android/build.gradle +++ b/flutter/android/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.2.2' + classpath 'com.android.tools.build:gradle:7.4.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -61,6 +61,7 @@ android { dependencies { api 'io.sentry:sentry-android:7.6.0' + implementation 'io.sentry:sentry-replay:1.0.0' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" // Required -- JUnit 4 framework diff --git a/flutter/example/android/app/build.gradle b/flutter/example/android/app/build.gradle index fc53460b79..83efbe98da 100644 --- a/flutter/example/android/app/build.gradle +++ b/flutter/example/android/app/build.gradle @@ -40,7 +40,7 @@ android { languageVersion = "1.4" } - compileSdkVersion 33 + compileSdkVersion 34 sourceSets { main.java.srcDirs += 'src/main/kotlin' diff --git a/flutter/example/android/build.gradle b/flutter/example/android/build.gradle index 50bb21eda7..bdd8d62e86 100644 --- a/flutter/example/android/build.gradle +++ b/flutter/example/android/build.gradle @@ -8,7 +8,7 @@ buildscript { dependencies { classpath 'io.sentry:sentry-android-gradle-plugin:4.2.0' - classpath 'com.android.tools.build:gradle:7.2.2' + classpath 'com.android.tools.build:gradle:7.4.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'io.github.howardpang:androidNativeBundle:1.1.3' } diff --git a/flutter/example/android/settings.gradle b/flutter/example/android/settings.gradle index 44e62bcf06..02c088c00f 100644 --- a/flutter/example/android/settings.gradle +++ b/flutter/example/android/settings.gradle @@ -9,3 +9,11 @@ localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } def flutterSdkPath = properties.getProperty("flutter.sdk") assert flutterSdkPath != null, "flutter.sdk not set in local.properties" apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" + +includeBuild("c:/dev/sentry-java") { + dependencySubstitution { + substitute(module("io.sentry:sentry")).using(project(":sentry")) + substitute(module("io.sentry:sentry-android-core")).using(project(":sentry-android-core")) + substitute(module("io.sentry:sentry-replay")).using(project(":sentry-android-replay")) + } +} diff --git a/min_version_test/android/build.gradle b/min_version_test/android/build.gradle index 70ef661422..4b30292ebe 100644 --- a/min_version_test/android/build.gradle +++ b/min_version_test/android/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.2.2' + classpath 'com.android.tools.build:gradle:7.4.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } From 2f556199bfd4ee6b03ce96a6c457446fad452b00 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 4 Apr 2024 16:17:20 +0200 Subject: [PATCH 03/74] tmp: use relative path to sentry-java --- flutter/example/android/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/example/android/settings.gradle b/flutter/example/android/settings.gradle index 02c088c00f..32d3e9e8b0 100644 --- a/flutter/example/android/settings.gradle +++ b/flutter/example/android/settings.gradle @@ -10,7 +10,7 @@ def flutterSdkPath = properties.getProperty("flutter.sdk") assert flutterSdkPath != null, "flutter.sdk not set in local.properties" apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" -includeBuild("c:/dev/sentry-java") { +includeBuild("../../../../sentry-java") { dependencySubstitution { substitute(module("io.sentry:sentry")).using(project(":sentry")) substitute(module("io.sentry:sentry-android-core")).using(project(":sentry-android-core")) From 209ca4125a3e0e8972000b35c2ac02158fe54a1d Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 4 Apr 2024 16:34:28 +0200 Subject: [PATCH 04/74] tmp: local java build patches --- flutter/android/build.gradle | 4 +++- .../android/app/src/main/cpp/native-sample.cpp | 17 +++++++++-------- flutter/example/android/build.gradle | 2 +- flutter/example/android/settings.gradle | 2 ++ min_version_test/android/build.gradle | 2 +- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/flutter/android/build.gradle b/flutter/android/build.gradle index c13508faaa..c8bffbb2a5 100644 --- a/flutter/android/build.gradle +++ b/flutter/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.21' + ext.kotlin_version = '1.8.0' repositories { google() mavenCentral() @@ -61,6 +61,8 @@ android { dependencies { api 'io.sentry:sentry-android:7.6.0' + implementation 'io.sentry:sentry-android-core:7.6.0' + implementation 'io.sentry:sentry-android-ndk:7.6.0' implementation 'io.sentry:sentry-replay:1.0.0' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" diff --git a/flutter/example/android/app/src/main/cpp/native-sample.cpp b/flutter/example/android/app/src/main/cpp/native-sample.cpp index deb1bc4e74..8b561d15c4 100644 --- a/flutter/example/android/app/src/main/cpp/native-sample.cpp +++ b/flutter/example/android/app/src/main/cpp/native-sample.cpp @@ -1,6 +1,6 @@ #include #include -#include +// #include #define TAG "sentry-sample" @@ -13,13 +13,14 @@ JNIEXPORT void JNICALL Java_io_sentry_samples_flutter_MainActivity_crash(JNIEnv } JNIEXPORT void JNICALL Java_io_sentry_samples_flutter_MainActivity_message(JNIEnv *env, jclass cls) { - // __android_log_print(ANDROID_LOG_WARN, TAG, "Sending message."); - sentry_value_t event = sentry_value_new_message_event( - /* level */ SENTRY_LEVEL_INFO, - /* logger */ "native", - /* message */ "message from C++!" - ); - sentry_capture_event(event); + // FIXME temporarily removed to use local sentry-java build + // // __android_log_print(ANDROID_LOG_WARN, TAG, "Sending message."); + // sentry_value_t event = sentry_value_new_message_event( + // /* level */ SENTRY_LEVEL_INFO, + // /* logger */ "native", + // /* message */ "message from C++!" + // ); + // sentry_capture_event(event); } } diff --git a/flutter/example/android/build.gradle b/flutter/example/android/build.gradle index bdd8d62e86..6d312c03b6 100644 --- a/flutter/example/android/build.gradle +++ b/flutter/example/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.21' + ext.kotlin_version = '1.8.0' repositories { google() diff --git a/flutter/example/android/settings.gradle b/flutter/example/android/settings.gradle index 32d3e9e8b0..df0d4c80c0 100644 --- a/flutter/example/android/settings.gradle +++ b/flutter/example/android/settings.gradle @@ -13,7 +13,9 @@ apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gra includeBuild("../../../../sentry-java") { dependencySubstitution { substitute(module("io.sentry:sentry")).using(project(":sentry")) + substitute(module("io.sentry:sentry-android")).using(project(":sentry-android")) substitute(module("io.sentry:sentry-android-core")).using(project(":sentry-android-core")) + substitute(module("io.sentry:sentry-android-ndk")).using(project(":sentry-android-ndk")) substitute(module("io.sentry:sentry-replay")).using(project(":sentry-android-replay")) } } diff --git a/min_version_test/android/build.gradle b/min_version_test/android/build.gradle index 4b30292ebe..e269d91443 100644 --- a/min_version_test/android/build.gradle +++ b/min_version_test/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.21' + ext.kotlin_version = '1.8.0' repositories { google() mavenCentral() From a282fada38db1447d3ea127ed4068ca347cc660e Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 10 Apr 2024 21:06:16 +0200 Subject: [PATCH 05/74] replay options --- .../kotlin/io/sentry/flutter/SentryFlutter.kt | 27 +++++++ .../io/sentry/flutter/SentryFlutterTest.kt | 26 ++++++- .../integrations/native_sdk_integration.dart | 11 +++ flutter/lib/src/sentry_flutter_options.dart | 5 ++ flutter/lib/src/sentry_replay_options.dart | 70 +++++++++++++++++++ 5 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 flutter/lib/src/sentry_replay_options.dart diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutter.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutter.kt index 962414c6a9..eb925c4ac6 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutter.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutter.kt @@ -3,6 +3,7 @@ package io.sentry.flutter import io.sentry.SentryLevel import io.sentry.android.core.BuildConfig import io.sentry.android.core.SentryAndroidOptions +import io.sentry.SentryReplayOptions import io.sentry.protocol.SdkVersion import java.util.Locale @@ -117,6 +118,32 @@ class SentryFlutter( data.getIfNotNull("readTimeoutMillis") { options.readTimeoutMillis = it } + + data.getIfNotNull>("replay") { + updateReplayOptions(options.experimental.sessionReplay, it) + } + } + + fun updateReplayOptions(options: SentryReplayOptions, data: Map) { + options.sessionSampleRate = data["sessionSampleRate"] as? Double + options.errorSampleRate = data["errorSampleRate"] as? Double + + // Currently, these are read-only options + // data.getIfNotNull("bitRate") { + // options.bitRate = it + // } + // data.getIfNotNull("frameRate") { + // options.frameRate = it + // } + // data.getIfNotNull("errorReplayDurationMillis") { + // options.errorReplayDuration = it + // } + // data.getIfNotNull("sessionSegmentDurationMillis") { + // options.replayCacheDefaultLowerBound = it + // } + // data.getIfNotNull("sessionDurationMillis") { + // options.sessionDuration = it + // } } } diff --git a/flutter/android/src/test/kotlin/io/sentry/flutter/SentryFlutterTest.kt b/flutter/android/src/test/kotlin/io/sentry/flutter/SentryFlutterTest.kt index 3a5c8678f8..ad7b97cb9f 100644 --- a/flutter/android/src/test/kotlin/io/sentry/flutter/SentryFlutterTest.kt +++ b/flutter/android/src/test/kotlin/io/sentry/flutter/SentryFlutterTest.kt @@ -61,6 +61,21 @@ class SentryFlutterTest { assertEquals(9006, fixture.options.connectionTimeoutMillis) assertEquals(9007, fixture.options.readTimeoutMillis) + + assertEquals(0.5, fixture.options.experimental.sessionReplay.sessionSampleRate) + assertEquals(0.6, fixture.options.experimental.sessionReplay.errorSampleRate) + + // TODO, these are currently read-only in SentryReplayOptions so we're testing the default values instead. + // assertEquals(10, fixture.options.experimental.sessionReplay.bitRate) + // assertEquals(20, fixture.options.experimental.sessionReplay.frameRate) + // assertEquals(1000L, fixture.options.experimental.sessionReplay.errorReplayDuration) + // assertEquals(500L, fixture.options.experimental.sessionReplay.sessionSegmentDuration) + // assertEquals(10_000L, fixture.options.experimental.sessionReplay.sessionDuration) + assertEquals(100_000, fixture.options.experimental.sessionReplay.bitRate) + assertEquals(1, fixture.options.experimental.sessionReplay.frameRate) + assertEquals(30_000L, fixture.options.experimental.sessionReplay.errorReplayDuration) + assertEquals(5000L, fixture.options.experimental.sessionReplay.sessionSegmentDuration) + assertEquals(60 * 60 * 1000L, fixture.options.experimental.sessionReplay.sessionDuration) } @Test @@ -127,7 +142,16 @@ class Fixture { "maxAttachmentSize" to 9005L, "enableAutoPerformanceTracing" to true, "connectionTimeoutMillis" to 9006, - "readTimeoutMillis" to 9007 + "readTimeoutMillis" to 9007, + "replay" to mapOf( + "sessionSampleRate" to 0.5, + "errorSampleRate" to 0.6, + "bitRate" to 10, + "frameRate" to 20, + "errorReplayDurationMillis" to 1000L, + "sessionSegmentDurationMillis" to 500L, + "sessionDurationMillis" to 10_000L, + ) ) fun getSut(): SentryFlutter { diff --git a/flutter/lib/src/integrations/native_sdk_integration.dart b/flutter/lib/src/integrations/native_sdk_integration.dart index 715a821cf9..1449320989 100644 --- a/flutter/lib/src/integrations/native_sdk_integration.dart +++ b/flutter/lib/src/integrations/native_sdk_integration.dart @@ -54,6 +54,17 @@ class NativeSdkIntegration implements Integration { 'readTimeoutMillis': options.readTimeout.inMilliseconds, 'appHangTimeoutIntervalMillis': options.appHangTimeoutInterval.inMilliseconds, + 'replay': { + 'sessionSampleRate': options.replay.sessionSampleRate, + 'errorSampleRate': options.replay.errorSampleRate, + 'bitRate': options.replay.bitRate, + 'frameRate': options.replay.frameRate, + 'errorReplayDurationMillis': + options.replay.errorReplayDuration.inMilliseconds, + 'sessionSegmentDurationMillis': + options.replay.sessionSegmentDuration.inMilliseconds, + 'sessionDuration': options.replay.sessionDuration.inMilliseconds + }, }); options.sdk.addIntegration('nativeSdkIntegration'); diff --git a/flutter/lib/src/sentry_flutter_options.dart b/flutter/lib/src/sentry_flutter_options.dart index ae83de611e..24f58a975b 100644 --- a/flutter/lib/src/sentry_flutter_options.dart +++ b/flutter/lib/src/sentry_flutter_options.dart @@ -10,6 +10,7 @@ import 'screenshot/sentry_screenshot_quality.dart'; import 'event_processor/screenshot_event_processor.dart'; import 'screenshot/sentry_screenshot_widget.dart'; import 'sentry_flutter.dart'; +import 'sentry_replay_options.dart'; import 'user_interaction/sentry_user_interaction_widget.dart'; /// This class adds options which are only available in a Flutter environment. @@ -305,6 +306,10 @@ class SentryFlutterOptions extends SentryOptions { /// The [navigatorKey] is used to add information of the currently used locale to the contexts. GlobalKey? navigatorKey; + + /// Configuration of the experimental replay feature. + @experimental + final SentryReplayOptions replay = SentryReplayOptions(); } /// Callback being executed in [ScreenshotEventProcessor], deciding if a diff --git a/flutter/lib/src/sentry_replay_options.dart b/flutter/lib/src/sentry_replay_options.dart new file mode 100644 index 0000000000..198fa895bf --- /dev/null +++ b/flutter/lib/src/sentry_replay_options.dart @@ -0,0 +1,70 @@ +import 'package:meta/meta.dart'; + +/// Configuration of the experimental replay feature. +class SentryReplayOptions { + double? _sessionSampleRate; + + /// Indicates the percentage in which the replay for the session will be created. Specifying 0 + /// means never, 1.0 means always. The value needs to be >= 0.0 and <= 1.0 The default is null + /// (disabled). + double? get sessionSampleRate => _sessionSampleRate; + + /// Indicates the percentage in which the replay for the session will be created. Specifying 0 + /// means never, 1.0 means always. The value needs to be >= 0.0 and <= 1.0 The default is null + /// (disabled). + set sessionSampleRate(double? value) { + assert(value == null || (value >= 0 && value <= 1)); + _sessionSampleRate = value; + } + + double? _errorSampleRate; + + /// Indicates the percentage in which a 30 seconds replay will be send with error events. + /// Specifying 0 means never, 1.0 means always. The value needs to be >= 0.0 and <= 1.0. The + /// default is null (disabled). + double? get errorSampleRate => _errorSampleRate; + + /// Indicates the percentage in which a 30 seconds replay will be send with error events. + /// Specifying 0 means never, 1.0 means always. The value needs to be >= 0.0 and <= 1.0. The + /// default is null (disabled). + set errorSampleRate(double? value) { + assert(value == null || (value >= 0 && value <= 1)); + _errorSampleRate = value; + } + + // TODO implement in flutter + // /// Redact all text content. Draws a rectangle of text bounds with text color on top. By default + // /// only views extending TextView are redacted. + // /// Default is enabled. + // bool redactAllText = true; + + // TODO implement in flutter + // /// Redact all image content. Draws a rectangle of image bounds with image's dominant color on top. + // /// By default only views extending ImageView with BitmapDrawable or custom Drawable type are + // /// redacted. ColorDrawable, InsetDrawable, VectorDrawable are all considered non-PII, as they come + // /// from the apk. + // /// Default is enabled. + // bool redactAllImages = true; + + /// Defines the quality of the session replay. Higher bit rates have better replay quality, but + /// also affect the final payload size to transfer, defaults to 100kbps. + @internal + int bitRate = 100000; + + /// Number of frames per second of the replay. The bigger the number, the more accurate the replay + /// will be, but also more data to transfer and more CPU load, defaults to 1fps. + @internal + int frameRate = 1; + + /// The maximum duration of replays for error events. + @internal + Duration errorReplayDuration = Duration(seconds: 30); + + /// The maximum duration of the segment of a session replay, defaults to 5s. + @internal + Duration sessionSegmentDuration = Duration(seconds: 5); + + /// The maximum duration of a full session replay, defaults to 1h. + @internal + Duration sessionDuration = Duration(hours: 1); +} From 7d8d4e84fff4e6233d7bf799e6298fa00cf10812 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 11 Apr 2024 10:57:11 +0200 Subject: [PATCH 06/74] replay recorder --- flutter/lib/src/replay/recorder.dart | 96 +++++++++++++++++++ flutter/lib/src/replay/recorder_config.dart | 11 +++ .../src/replay/recorder_widget_filter.dart | 47 +++++++++ flutter/lib/src/replay/scheduler.dart | 49 ++++++++++ 4 files changed, 203 insertions(+) create mode 100644 flutter/lib/src/replay/recorder.dart create mode 100644 flutter/lib/src/replay/recorder_config.dart create mode 100644 flutter/lib/src/replay/recorder_widget_filter.dart create mode 100644 flutter/lib/src/replay/scheduler.dart diff --git a/flutter/lib/src/replay/recorder.dart b/flutter/lib/src/replay/recorder.dart new file mode 100644 index 0000000000..976357cb98 --- /dev/null +++ b/flutter/lib/src/replay/recorder.dart @@ -0,0 +1,96 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:flutter/rendering.dart'; +import 'package:meta/meta.dart'; + +import '../screenshot/sentry_screenshot_widget.dart'; +import 'recorder_config.dart'; +import 'recorder_widget_filter.dart'; +import 'scheduler.dart'; + +@internal +typedef ScreenshotRecorderCallback = Future Function(Image); + +@internal +class ScreenshotRecorder { + final ScreenshotRecorderConfig _config; + final ScreenshotRecorderCallback _callback; + late final Scheduler _scheduler; + final Paint _widgetObscurePaint = Paint()..color = const Color(0x7f000000); + + ScreenshotRecorder(this._config, this._callback) { + final frameDuration = Duration(milliseconds: 1000 ~/ _config.frameRate); + _scheduler = Scheduler(frameDuration, _capture); + } + + void start() => _scheduler.start(); + + void stop() => _scheduler.stop(); + + // TODO try-catch + Future _capture(Duration sinceSchedulerEpoch) async { + final context = sentryScreenshotWidgetGlobalKey.currentContext; + final renderObject = context?.findRenderObject(); + + if (context != null && renderObject is RenderRepaintBoundary) { + final watch = Stopwatch()..start(); + + // TODO downsize right away to the desired resolution: use _config + final width = renderObject.size.width.round(); + final height = renderObject.size.height.round(); + final pixelRatio = 1.0; + + final recorder = PictureRecorder(); + final canvas = Canvas(recorder); + + final futureImage = renderObject.toImage(pixelRatio: pixelRatio); + watch.printAndReset("renderObject.toImage()"); + + final filter = WidgetFilter(pixelRatio); + context.visitChildElements(filter.obscure); + watch.printAndReset("collect widget boundaries"); + + final image = await futureImage; + watch.printAndReset("await image"); + try { + canvas.drawImage(image, Offset.zero, Paint()); + watch.printAndReset("drawImage()"); + } finally { + image.dispose(); + } + + _obscureWidgets(canvas, filter.bounds); + watch.printAndReset("obscureWidgets()"); + + final picture = recorder.endRecording(); + watch.printAndReset("endRecording()"); + + try { + final finalImage = await picture.toImage(width, height); + watch.printAndReset("picture.toImage()"); + try { + await _callback(finalImage); + watch.printAndReset("callback()"); + } finally { + finalImage.dispose(); + } + } finally { + picture.dispose(); + } + } + } + + void _obscureWidgets(Canvas canvas, List widgetBounds) { + for (var bounds in widgetBounds) { + canvas.drawRect(bounds, _widgetObscurePaint); + } + } +} + +extension _WatchPrinter on Stopwatch { + void printAndReset(String message) { + print("$message: $elapsedMicroseconds us"); + reset(); + } +} diff --git a/flutter/lib/src/replay/recorder_config.dart b/flutter/lib/src/replay/recorder_config.dart new file mode 100644 index 0000000000..10959eb0d8 --- /dev/null +++ b/flutter/lib/src/replay/recorder_config.dart @@ -0,0 +1,11 @@ +import 'package:meta/meta.dart'; + +@internal +class ScreenshotRecorderConfig { + final int width; + final int height; + final int frameRate; + final int bitRate; + + ScreenshotRecorderConfig(this.width, this.height, this.frameRate, this.bitRate) +} diff --git a/flutter/lib/src/replay/recorder_widget_filter.dart b/flutter/lib/src/replay/recorder_widget_filter.dart new file mode 100644 index 0000000000..da1ebee2c6 --- /dev/null +++ b/flutter/lib/src/replay/recorder_widget_filter.dart @@ -0,0 +1,47 @@ +import 'package:flutter/widgets.dart'; +import 'package:meta/meta.dart'; + +@internal +class WidgetFilter { + final double _pixelRatio; + final List bounds = []; + + WidgetFilter(this._pixelRatio); + + void obscure(Element element) { + final widget = element.widget; + + if (widget is Visibility && !widget.visible) { + return; + } + if (widget is Opacity && widget.opacity <= 0.0) { + return; + } + + if (_shouldObscure(widget)) { + final renderObject = element.renderObject; + if (renderObject is RenderBox) { + final offset = renderObject.localToGlobal(Offset.zero); + final size = element.size; + if (size != null) { + bounds.add(Rect.fromLTWH( + offset.dx * _pixelRatio, + offset.dy * _pixelRatio, + size.width * _pixelRatio, + size.height * _pixelRatio, + )); + return; + } + } else { + // TODO fix logging + print( + "Cannot obscure widget $widget, it's renderObject is not a RenderBox"); + } + } + + element.visitChildElements(obscure); + } + + bool _shouldObscure(Widget widget) => + widget is Image || widget is RichText || widget is TextBox; +} diff --git a/flutter/lib/src/replay/scheduler.dart b/flutter/lib/src/replay/scheduler.dart new file mode 100644 index 0000000000..6d5ba2157c --- /dev/null +++ b/flutter/lib/src/replay/scheduler.dart @@ -0,0 +1,49 @@ +import 'package:flutter/rendering.dart'; +import 'package:meta/meta.dart'; + +@internal +typedef SchedulerCallback = Future Function(Duration); + +/// This is a low-priority scheduler. +/// We're not using Timer.periodic() because it may schedule a callback +/// even if the previous call hasn't finished (or started) yet. +/// Instead, we manually schedule a callback with a given delay after the +/// previous callback finished. Therefore, if the capture takes too long, we +/// won't overload the system. We sacrifice the frame rate for performance. +@internal +class Scheduler { + final SchedulerCallback _callback; + final Duration _interval; + bool _running = false; + bool _scheduled = false; + + Scheduler(this._interval, this._callback); + + void start() { + _running = true; + if (!_scheduled) { + _runAfterNextFrame(); + } + } + + void stop() { + _running = false; + } + + void _scheduleNext() { + if (!_scheduled) { + _scheduled = true; + Future.delayed(_interval, _runAfterNextFrame); + } + } + + void _runAfterNextFrame() { + RendererBinding.instance.addPostFrameCallback(_run); + } + + void _run(Duration sinceSchedulerEpoch) { + _scheduled = false; + if (!_running) return; + _callback(sinceSchedulerEpoch).then((_) => _scheduleNext()); + } +} From 3444961f2a6282e21895162c9aa64678e1f88329 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 11 Apr 2024 15:14:55 +0200 Subject: [PATCH 07/74] wip: JNI native bindings --- flutter/ffi-jni.yaml | 19 +++++++++++++++++++ flutter/pubspec.yaml | 2 ++ 2 files changed, 21 insertions(+) create mode 100644 flutter/ffi-jni.yaml diff --git a/flutter/ffi-jni.yaml b/flutter/ffi-jni.yaml new file mode 100644 index 0000000000..a5594b4b90 --- /dev/null +++ b/flutter/ffi-jni.yaml @@ -0,0 +1,19 @@ +android_sdk_config: + add_gradle_deps: true + android_example: 'example/' + +# summarizer: +# backend: asm + +output: + c: + library_name: sentry_android_binding + path: android/src/main/cpp/ + dart: + path: lib/src/native/jni/binding.dart + structure: single_file + +log_level: all + +classes: + - io.sentry.Sentry diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 30ccca3dcc..cce1ddc993 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -19,6 +19,7 @@ dependencies: package_info_plus: '>=1.0.0 <7.0.0' meta: ^1.3.0 ffi: ^2.0.0 + jni: ^0.8.0 dev_dependencies: build_runner: ^2.4.2 @@ -31,6 +32,7 @@ dev_dependencies: remove_from_coverage: ^2.0.0 flutter_localizations: sdk: flutter + jnigen: ^0.8.0 flutter: plugin: From ca03c95d2c5de4622ef7a99b2b88c1be398f680c Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 17 Apr 2024 09:24:02 +0200 Subject: [PATCH 08/74] use compatible jnigen --- flutter/pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index cce1ddc993..5b5a8de235 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: package_info_plus: '>=1.0.0 <7.0.0' meta: ^1.3.0 ffi: ^2.0.0 - jni: ^0.8.0 + jni: ^0.5.0 dev_dependencies: build_runner: ^2.4.2 @@ -32,7 +32,7 @@ dev_dependencies: remove_from_coverage: ^2.0.0 flutter_localizations: sdk: flutter - jnigen: ^0.8.0 + jnigen: ^0.5.0 flutter: plugin: From 0b06dd5bf15a21799634ce1f17ae4cc84116fdcc Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 17 Apr 2024 09:47:54 +0200 Subject: [PATCH 09/74] add missing gradlew to flutter/android --- flutter/android/gradlew | 1 + flutter/android/gradlew.bat | 1 + 2 files changed, 2 insertions(+) create mode 120000 flutter/android/gradlew create mode 120000 flutter/android/gradlew.bat diff --git a/flutter/android/gradlew b/flutter/android/gradlew new file mode 120000 index 0000000000..a9a4ed1b8d --- /dev/null +++ b/flutter/android/gradlew @@ -0,0 +1 @@ +../example/android/gradlew \ No newline at end of file diff --git a/flutter/android/gradlew.bat b/flutter/android/gradlew.bat new file mode 120000 index 0000000000..2418d41fea --- /dev/null +++ b/flutter/android/gradlew.bat @@ -0,0 +1 @@ +../example/android/gradlew.bat \ No newline at end of file From 8bf52d870d4b491f420bad1cc0cc6ea79ab336b0 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 17 Apr 2024 16:06:31 +0200 Subject: [PATCH 10/74] replay recorder JNI binding code --- flutter/android/gradle.properties | 1 + flutter/android/src/main/cpp/.clang-format | 15 + flutter/android/src/main/cpp/CMakeLists.txt | 32 ++ flutter/android/src/main/cpp/dartjni.h | 378 ++++++++++++++++ .../src/main/cpp/sentry_android_binding.c | 407 ++++++++++++++++++ flutter/ffi-jni.yaml | 5 +- flutter/lib/src/native/java/binding.dart | 374 ++++++++++++++++ 7 files changed, 1210 insertions(+), 2 deletions(-) create mode 100644 flutter/android/src/main/cpp/.clang-format create mode 100644 flutter/android/src/main/cpp/CMakeLists.txt create mode 100644 flutter/android/src/main/cpp/dartjni.h create mode 100644 flutter/android/src/main/cpp/sentry_android_binding.c create mode 100644 flutter/lib/src/native/java/binding.dart diff --git a/flutter/android/gradle.properties b/flutter/android/gradle.properties index 8bd86f6805..d9cf55df7c 100644 --- a/flutter/android/gradle.properties +++ b/flutter/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true diff --git a/flutter/android/src/main/cpp/.clang-format b/flutter/android/src/main/cpp/.clang-format new file mode 100644 index 0000000000..a256c2f097 --- /dev/null +++ b/flutter/android/src/main/cpp/.clang-format @@ -0,0 +1,15 @@ +# From dart SDK: https://github.com/dart-lang/sdk/blob/main/.clang-format + +# Defines the Chromium style for automatic reformatting. +# http://clang.llvm.org/docs/ClangFormatStyleOptions.html +BasedOnStyle: Chromium + +# clang-format doesn't seem to do a good job of this for longer comments. +ReflowComments: 'false' + +# We have lots of these. Though we need to put them all in curly braces, +# clang-format can't do that. +AllowShortIfStatementsOnASingleLine: 'true' + +# Put escaped newlines into the rightmost column. +AlignEscapedNewlinesLeft: false diff --git a/flutter/android/src/main/cpp/CMakeLists.txt b/flutter/android/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000000..a0d5849c59 --- /dev/null +++ b/flutter/android/src/main/cpp/CMakeLists.txt @@ -0,0 +1,32 @@ +# jni_native_build (Build with jni:setup. Do not delete this line.) + +# The Flutter tooling requires that developers have CMake 3.10 or later +# installed. You should not increase this version, as doing so will cause +# the plugin to fail to compile for some customers of the plugin. +cmake_minimum_required(VERSION 3.10) + +project(sentry_android_binding VERSION 0.0.1 LANGUAGES C) + +add_library(sentry_android_binding SHARED + "./sentry_android_binding.c" +) + +set_target_properties(sentry_android_binding PROPERTIES + OUTPUT_NAME "sentry_android_binding" +) + +target_compile_definitions(sentry_android_binding PUBLIC DART_SHARED_LIB) + +if(WIN32) + set_target_properties(${TARGET_NAME} PROPERTIES + LINK_FLAGS "/DELAYLOAD:jvm.dll") +endif() + +if (ANDROID) + target_link_libraries(sentry_android_binding log) +else() + find_package(Java REQUIRED) + find_package(JNI REQUIRED) + include_directories(${JNI_INCLUDE_DIRS}) + target_link_libraries(sentry_android_binding ${JNI_LIBRARIES}) +endif() diff --git a/flutter/android/src/main/cpp/dartjni.h b/flutter/android/src/main/cpp/dartjni.h new file mode 100644 index 0000000000..c0713af53a --- /dev/null +++ b/flutter/android/src/main/cpp/dartjni.h @@ -0,0 +1,378 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#pragma once + +// Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. +#include +#include +#include +#include + +#if _WIN32 +#include +#else +#include +#include +#endif + +#if _WIN32 +#define FFI_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FFI_PLUGIN_EXPORT +#endif + +#if defined _WIN32 +#define thread_local __declspec(thread) +#else +#define thread_local __thread +#endif + +#ifdef __ANDROID__ +#include +#endif + +#ifdef __ANDROID__ +#define __ENVP_CAST (JNIEnv**) +#else +#define __ENVP_CAST (void**) +#endif + +/// Locking functions for windows and pthread. + +#if defined _WIN32 +#include + +typedef CRITICAL_SECTION MutexLock; + +static inline void init_lock(MutexLock* lock) { + InitializeCriticalSection(lock); +} + +static inline void acquire_lock(MutexLock* lock) { + EnterCriticalSection(lock); +} + +static inline void release_lock(MutexLock* lock) { + LeaveCriticalSection(lock); +} + +static inline void _destroyLock(MutexLock* lock) { + DeleteCriticalSection(lock); +} + +#elif defined __DARWIN__ || defined __LINUX__ || defined __ANDROID__ || \ + defined __GNUC__ +#include + +typedef pthread_mutex_t MutexLock; + +static inline void init_lock(MutexLock* lock) { + pthread_mutex_init(lock, NULL); +} + +static inline void acquire_lock(MutexLock* lock) { + pthread_mutex_lock(lock); +} + +static inline void release_lock(MutexLock* lock) { + pthread_mutex_unlock(lock); +} + +static inline void _destroyLock(MutexLock* lock) { + pthread_mutex_destroy(lock); +} + +#else + +#error "No locking support; Possibly unsupported platform" + +#endif + +typedef struct JniLocks { + MutexLock classLoadingLock; + MutexLock methodLoadingLock; + MutexLock fieldLoadingLock; +} JniLocks; + +/// Represents the error when dart-jni layer has already spawned singleton VM. +#define DART_JNI_SINGLETON_EXISTS (-99); + +/// Stores the global state of the JNI. +typedef struct JniContext { + JavaVM* jvm; + jobject classLoader; + jmethodID loadClassMethod; + jobject currentActivity; + jobject appContext; + JniLocks locks; +} JniContext; + +// jniEnv for this thread, used by inline functions in this header, +// therefore declared as extern. +extern thread_local JNIEnv* jniEnv; + +extern JniContext* jni; + +/// Types used by JNI API to distinguish between primitive types. +enum JniType { + booleanType = 0, + byteType = 1, + shortType = 2, + charType = 3, + intType = 4, + longType = 5, + floatType = 6, + doubleType = 7, + objectType = 8, + voidType = 9, +}; + +/// Result type for use by JNI. +/// +/// If [exception] is null, it means the result is valid. +/// It's assumed that the caller knows the expected type in [result]. +typedef struct JniResult { + jvalue value; + jthrowable exception; +} JniResult; + +/// Similar to [JniResult] but for class lookups. +typedef struct JniClassLookupResult { + jclass value; + jthrowable exception; +} JniClassLookupResult; + +/// Similar to [JniResult] but for method/field ID lookups. +typedef struct JniPointerResult { + const void* value; + jthrowable exception; +} JniPointerResult; + +/// JniExceptionDetails holds 2 jstring objects, one is the result of +/// calling `toString` on exception object, other is stack trace; +typedef struct JniExceptionDetails { + jstring message; + jstring stacktrace; +} JniExceptionDetails; + +/// This struct contains functions which wrap method call / field access conveniently along with +/// exception checking. +/// +/// Flutter embedding checks for pending JNI exceptions before an FFI transition, which requires us +/// to check for and clear the exception before returning to dart code, which requires these functions +/// to return result types. +typedef struct JniAccessorsStruct { + JniClassLookupResult (*getClass)(char* internalName); + JniPointerResult (*getFieldID)(jclass cls, char* fieldName, char* signature); + JniPointerResult (*getStaticFieldID)(jclass cls, + char* fieldName, + char* signature); + JniPointerResult (*getMethodID)(jclass cls, + char* methodName, + char* signature); + JniPointerResult (*getStaticMethodID)(jclass cls, + char* methodName, + char* signature); + JniResult (*newObject)(jclass cls, jmethodID ctor, jvalue* args); + JniResult (*newPrimitiveArray)(jsize length, int type); + JniResult (*newObjectArray)(jsize length, + jclass elementClass, + jobject initialElement); + JniResult (*getArrayElement)(jarray array, int index, int type); + JniResult (*callMethod)(jobject obj, + jmethodID methodID, + int callType, + jvalue* args); + JniResult (*callStaticMethod)(jclass cls, + jmethodID methodID, + int callType, + jvalue* args); + JniResult (*getField)(jobject obj, jfieldID fieldID, int callType); + JniResult (*getStaticField)(jclass cls, jfieldID fieldID, int callType); + JniExceptionDetails (*getExceptionDetails)(jthrowable exception); +} JniAccessorsStruct; + +FFI_PLUGIN_EXPORT JniAccessorsStruct* GetAccessors(); + +FFI_PLUGIN_EXPORT JavaVM* GetJavaVM(void); + +FFI_PLUGIN_EXPORT JNIEnv* GetJniEnv(void); + +/// Spawn a JVM with given arguments. +/// +/// Returns JNI_OK on success, and one of the documented JNI error codes on +/// failure. It returns DART_JNI_SINGLETON_EXISTS if an attempt to spawn multiple +/// JVMs is made, even if the underlying API potentially supports multiple VMs. +FFI_PLUGIN_EXPORT int SpawnJvm(JavaVMInitArgs* args); + +/// Load class through platform-specific mechanism. +/// +/// Currently uses application classloader on android, +/// and JNIEnv->FindClass on other platforms. +FFI_PLUGIN_EXPORT jclass FindClass(const char* name); + +/// Returns Application classLoader (on Android), +/// which can be used to load application and platform classes. +/// +/// On other platforms, NULL is returned. +FFI_PLUGIN_EXPORT jobject GetClassLoader(void); + +/// Returns application context on Android. +/// +/// On other platforms, NULL is returned. +FFI_PLUGIN_EXPORT jobject GetApplicationContext(void); + +/// Returns current activity of the app on Android. +FFI_PLUGIN_EXPORT jobject GetCurrentActivity(void); + +static inline void attach_thread() { + if (jniEnv == NULL) { + (*jni->jvm)->AttachCurrentThread(jni->jvm, __ENVP_CAST & jniEnv, NULL); + } +} + +/// Load class into [cls] using platform specific mechanism +static inline void load_class_platform(jclass* cls, const char* name) { +#ifdef __ANDROID__ + jstring className = (*jniEnv)->NewStringUTF(jniEnv, name); + *cls = (*jniEnv)->CallObjectMethod(jniEnv, jni->classLoader, + jni->loadClassMethod, className); + (*jniEnv)->DeleteLocalRef(jniEnv, className); +#else + *cls = (*jniEnv)->FindClass(jniEnv, name); +#endif +} + +static inline void load_class_local_ref(jclass* cls, const char* name) { + if (*cls == NULL) { + acquire_lock(&jni->locks.classLoadingLock); + if (*cls == NULL) { + load_class_platform(cls, name); + } + release_lock(&jni->locks.classLoadingLock); + } +} + +static inline void load_class_global_ref(jclass* cls, const char* name) { + if (*cls == NULL) { + jclass tmp = NULL; + acquire_lock(&jni->locks.classLoadingLock); + if (*cls == NULL) { + load_class_platform(&tmp, name); + if (!(*jniEnv)->ExceptionCheck(jniEnv)) { + *cls = (*jniEnv)->NewGlobalRef(jniEnv, tmp); + (*jniEnv)->DeleteLocalRef(jniEnv, tmp); + } + } + release_lock(&jni->locks.classLoadingLock); + } +} + +static inline void load_method(jclass cls, + jmethodID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + acquire_lock(&jni->locks.methodLoadingLock); + if (*res == NULL) { + *res = (*jniEnv)->GetMethodID(jniEnv, cls, name, sig); + } + release_lock(&jni->locks.methodLoadingLock); + } +} + +static inline void load_static_method(jclass cls, + jmethodID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + acquire_lock(&jni->locks.methodLoadingLock); + if (*res == NULL) { + *res = (*jniEnv)->GetStaticMethodID(jniEnv, cls, name, sig); + } + release_lock(&jni->locks.methodLoadingLock); + } +} + +static inline void load_field(jclass cls, + jfieldID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + acquire_lock(&jni->locks.fieldLoadingLock); + if (*res == NULL) { + *res = (*jniEnv)->GetFieldID(jniEnv, cls, name, sig); + } + release_lock(&jni->locks.fieldLoadingLock); + } +} + +static inline void load_static_field(jclass cls, + jfieldID* res, + const char* name, + const char* sig) { + if (*res == NULL) { + acquire_lock(&jni->locks.fieldLoadingLock); + if (*res == NULL) { + *res = (*jniEnv)->GetStaticFieldID(jniEnv, cls, name, sig); + } + release_lock(&jni->locks.fieldLoadingLock); + } +} + +static inline jobject to_global_ref(jobject ref) { + jobject g = (*jniEnv)->NewGlobalRef(jniEnv, ref); + (*jniEnv)->DeleteLocalRef(jniEnv, ref); + return g; +} + +// These functions are useful for C+Dart bindings, and not required for pure dart bindings. + +FFI_PLUGIN_EXPORT JniContext* GetJniContextPtr(); + +/// For use by jni_gen's generated code +/// don't use these. + +// these 2 fn ptr vars will be defined by generated code library +extern JniContext* (*context_getter)(void); +extern JNIEnv* (*env_getter)(void); + +// this function will be exported by generated code library +// it will set above 2 variables. +FFI_PLUGIN_EXPORT void setJniGetters(struct JniContext* (*cg)(void), + JNIEnv* (*eg)(void)); + +static inline void load_env() { + if (jniEnv == NULL) { + jni = context_getter(); + jniEnv = env_getter(); + } +} + +static inline jthrowable check_exception() { + jthrowable exception = (*jniEnv)->ExceptionOccurred(jniEnv); + if (exception != NULL) (*jniEnv)->ExceptionClear(jniEnv); + if (exception == NULL) return NULL; + return to_global_ref(exception); +} + +static inline JniResult to_global_ref_result(jobject ref) { + JniResult result; + result.exception = check_exception(); + if (result.exception == NULL) { + result.value.l = to_global_ref(ref); + } + return result; +} + +FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); + +JNIEXPORT void JNICALL +Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, + jobject thiz, + jlong port, + jobject result); +FFI_PLUGIN_EXPORT +JniResult PortContinuation__ctor(int64_t j); diff --git a/flutter/android/src/main/cpp/sentry_android_binding.c b/flutter/android/src/main/cpp/sentry_android_binding.c new file mode 100644 index 0000000000..ad564935c0 --- /dev/null +++ b/flutter/android/src/main/cpp/sentry_android_binding.c @@ -0,0 +1,407 @@ +// Autogenerated by jnigen. DO NOT EDIT! + +#include +#include "dartjni.h" +#include "jni.h" + +thread_local JNIEnv* jniEnv; +JniContext* jni; + +JniContext* (*context_getter)(void); +JNIEnv* (*env_getter)(void); + +void setJniGetters(JniContext* (*cg)(void), JNIEnv* (*eg)(void)) { + context_getter = cg; + env_getter = eg; +} + +// io.sentry.android.replay.Recorder +jclass _c_Recorder = NULL; + +jmethodID _m_Recorder__start = NULL; +FFI_PLUGIN_EXPORT +JniResult Recorder__start(jobject self_, jobject screenshotRecorderConfig) { + load_env(); + load_class_global_ref(&_c_Recorder, "io/sentry/android/replay/Recorder"); + if (_c_Recorder == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_Recorder, &_m_Recorder__start, "start", + "(Lio/sentry/android/replay/ScreenshotRecorderConfig;)V"); + if (_m_Recorder__start == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Recorder__start, + screenshotRecorderConfig); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jmethodID _m_Recorder__resume = NULL; +FFI_PLUGIN_EXPORT +JniResult Recorder__resume(jobject self_) { + load_env(); + load_class_global_ref(&_c_Recorder, "io/sentry/android/replay/Recorder"); + if (_c_Recorder == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_Recorder, &_m_Recorder__resume, "resume", "()V"); + if (_m_Recorder__resume == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Recorder__resume); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jmethodID _m_Recorder__pause = NULL; +FFI_PLUGIN_EXPORT +JniResult Recorder__pause(jobject self_) { + load_env(); + load_class_global_ref(&_c_Recorder, "io/sentry/android/replay/Recorder"); + if (_c_Recorder == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_Recorder, &_m_Recorder__pause, "pause", "()V"); + if (_m_Recorder__pause == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Recorder__pause); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jmethodID _m_Recorder__stop = NULL; +FFI_PLUGIN_EXPORT +JniResult Recorder__stop(jobject self_) { + load_env(); + load_class_global_ref(&_c_Recorder, "io/sentry/android/replay/Recorder"); + if (_c_Recorder == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_Recorder, &_m_Recorder__stop, "stop", "()V"); + if (_m_Recorder__stop == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Recorder__stop); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +// io.sentry.android.replay.ScreenshotRecorderConfig +jclass _c_ScreenshotRecorderConfig = NULL; + +jmethodID _m_ScreenshotRecorderConfig__ctor = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig__ctor(int32_t i, + int32_t i1, + float f, + float f1, + int32_t i2, + int32_t i3) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig, &_m_ScreenshotRecorderConfig__ctor, + "", "(IIFFII)V"); + if (_m_ScreenshotRecorderConfig__ctor == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->NewObject(jniEnv, _c_ScreenshotRecorderConfig, + _m_ScreenshotRecorderConfig__ctor, i, + i1, f, f1, i2, i3); + return to_global_ref_result(_result); +} + +jmethodID _m_ScreenshotRecorderConfig__getRecordingWidth = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig__getRecordingWidth(jobject self_) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__getRecordingWidth, + "getRecordingWidth", "()I"); + if (_m_ScreenshotRecorderConfig__getRecordingWidth == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int32_t _result = (*jniEnv)->CallIntMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__getRecordingWidth); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; +} + +jmethodID _m_ScreenshotRecorderConfig__getRecordingHeight = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig__getRecordingHeight(jobject self_) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__getRecordingHeight, + "getRecordingHeight", "()I"); + if (_m_ScreenshotRecorderConfig__getRecordingHeight == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int32_t _result = (*jniEnv)->CallIntMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__getRecordingHeight); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; +} + +jmethodID _m_ScreenshotRecorderConfig__getScaleFactorX = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig__getScaleFactorX(jobject self_) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__getScaleFactorX, "getScaleFactorX", + "()F"); + if (_m_ScreenshotRecorderConfig__getScaleFactorX == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + float _result = (*jniEnv)->CallFloatMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__getScaleFactorX); + return (JniResult){.value = {.f = _result}, .exception = check_exception()}; +} + +jmethodID _m_ScreenshotRecorderConfig__getScaleFactorY = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig__getScaleFactorY(jobject self_) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__getScaleFactorY, "getScaleFactorY", + "()F"); + if (_m_ScreenshotRecorderConfig__getScaleFactorY == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + float _result = (*jniEnv)->CallFloatMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__getScaleFactorY); + return (JniResult){.value = {.f = _result}, .exception = check_exception()}; +} + +jmethodID _m_ScreenshotRecorderConfig__getFrameRate = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig__getFrameRate(jobject self_) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__getFrameRate, "getFrameRate", + "()I"); + if (_m_ScreenshotRecorderConfig__getFrameRate == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int32_t _result = (*jniEnv)->CallIntMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__getFrameRate); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; +} + +jmethodID _m_ScreenshotRecorderConfig__getBitRate = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig__getBitRate(jobject self_) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__getBitRate, "getBitRate", "()I"); + if (_m_ScreenshotRecorderConfig__getBitRate == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int32_t _result = (*jniEnv)->CallIntMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__getBitRate); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; +} + +jmethodID _m_ScreenshotRecorderConfig__component1 = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig__component1(jobject self_) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__component1, "component1", "()I"); + if (_m_ScreenshotRecorderConfig__component1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int32_t _result = (*jniEnv)->CallIntMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__component1); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; +} + +jmethodID _m_ScreenshotRecorderConfig__component2 = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig__component2(jobject self_) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__component2, "component2", "()I"); + if (_m_ScreenshotRecorderConfig__component2 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int32_t _result = (*jniEnv)->CallIntMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__component2); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; +} + +jmethodID _m_ScreenshotRecorderConfig__component3 = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig__component3(jobject self_) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__component3, "component3", "()F"); + if (_m_ScreenshotRecorderConfig__component3 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + float _result = (*jniEnv)->CallFloatMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__component3); + return (JniResult){.value = {.f = _result}, .exception = check_exception()}; +} + +jmethodID _m_ScreenshotRecorderConfig__component4 = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig__component4(jobject self_) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__component4, "component4", "()F"); + if (_m_ScreenshotRecorderConfig__component4 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + float _result = (*jniEnv)->CallFloatMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__component4); + return (JniResult){.value = {.f = _result}, .exception = check_exception()}; +} + +jmethodID _m_ScreenshotRecorderConfig__component5 = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig__component5(jobject self_) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__component5, "component5", "()I"); + if (_m_ScreenshotRecorderConfig__component5 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int32_t _result = (*jniEnv)->CallIntMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__component5); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; +} + +jmethodID _m_ScreenshotRecorderConfig__component6 = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig__component6(jobject self_) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__component6, "component6", "()I"); + if (_m_ScreenshotRecorderConfig__component6 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int32_t _result = (*jniEnv)->CallIntMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__component6); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; +} + +jmethodID _m_ScreenshotRecorderConfig__copy = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig__copy(jobject self_, + int32_t i, + int32_t i1, + float f, + float f1, + int32_t i2, + int32_t i3) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig, &_m_ScreenshotRecorderConfig__copy, + "copy", + "(IIFFII)Lio/sentry/android/replay/ScreenshotRecorderConfig;"); + if (_m_ScreenshotRecorderConfig__copy == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__copy, i, i1, f, f1, i2, i3); + return to_global_ref_result(_result); +} + +jmethodID _m_ScreenshotRecorderConfig__toString1 = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig__toString1(jobject self_) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__toString1, "toString", + "()Ljava/lang/String;"); + if (_m_ScreenshotRecorderConfig__toString1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__toString1); + return to_global_ref_result(_result); +} + +jmethodID _m_ScreenshotRecorderConfig__hashCode1 = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig__hashCode1(jobject self_) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__hashCode1, "hashCode", "()I"); + if (_m_ScreenshotRecorderConfig__hashCode1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int32_t _result = (*jniEnv)->CallIntMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__hashCode1); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; +} + +jmethodID _m_ScreenshotRecorderConfig__equals1 = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig__equals1(jobject self_, jobject object) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__equals1, "equals", + "(Ljava/lang/Object;)Z"); + if (_m_ScreenshotRecorderConfig__equals1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = (*jniEnv)->CallBooleanMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__equals1, object); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jfieldID _f_ScreenshotRecorderConfig__Companion = NULL; +FFI_PLUGIN_EXPORT +JniResult get_ScreenshotRecorderConfig__Companion() { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_field( + _c_ScreenshotRecorderConfig, &_f_ScreenshotRecorderConfig__Companion, + "Companion", + "Lio/sentry/android/replay/ScreenshotRecorderConfig$Companion;"); + jobject _result = + (*jniEnv)->GetStaticObjectField(jniEnv, _c_ScreenshotRecorderConfig, + _f_ScreenshotRecorderConfig__Companion); + return to_global_ref_result(_result); +} diff --git a/flutter/ffi-jni.yaml b/flutter/ffi-jni.yaml index a5594b4b90..97d24eb096 100644 --- a/flutter/ffi-jni.yaml +++ b/flutter/ffi-jni.yaml @@ -10,10 +10,11 @@ output: library_name: sentry_android_binding path: android/src/main/cpp/ dart: - path: lib/src/native/jni/binding.dart + path: lib/src/native/java/binding.dart structure: single_file log_level: all classes: - - io.sentry.Sentry + - io.sentry.android.replay.Recorder + - io.sentry.android.replay.ScreenshotRecorderConfig diff --git a/flutter/lib/src/native/java/binding.dart b/flutter/lib/src/native/java/binding.dart new file mode 100644 index 0000000000..ef10f901f1 --- /dev/null +++ b/flutter/lib/src/native/java/binding.dart @@ -0,0 +1,374 @@ +// Autogenerated by jnigen. DO NOT EDIT! + +// ignore_for_file: annotate_overrides +// ignore_for_file: camel_case_extensions +// ignore_for_file: camel_case_types +// ignore_for_file: constant_identifier_names +// ignore_for_file: file_names +// ignore_for_file: no_leading_underscores_for_local_identifiers +// ignore_for_file: non_constant_identifier_names +// ignore_for_file: overridden_fields +// ignore_for_file: unnecessary_cast +// ignore_for_file: unused_element +// ignore_for_file: unused_field +// ignore_for_file: unused_import +// ignore_for_file: unused_shown_name + +import "dart:isolate" show ReceivePort; +import "dart:ffi" as ffi; +import "package:jni/internal_helpers_for_jnigen.dart"; +import "package:jni/jni.dart" as jni; + +// Auto-generated initialization code. + +final ffi.Pointer Function(String sym) jniLookup = + ProtectedJniExtensions.initGeneratedLibrary("sentry_android_binding"); + +/// from: io.sentry.android.replay.Recorder +class Recorder extends jni.JObject { + @override + late final jni.JObjType $type = type; + + Recorder.fromRef( + jni.JObjectPtr ref, + ) : super.fromRef(ref); + + /// The type which includes information such as the signature of this class. + static const type = $RecorderType(); + static final _start = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("Recorder__start") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public abstract void start(io.sentry.android.replay.ScreenshotRecorderConfig screenshotRecorderConfig) + void start( + ScreenshotRecorderConfig screenshotRecorderConfig, + ) { + return _start(reference, screenshotRecorderConfig.reference).check(); + } + + static final _resume = jniLookup< + ffi + .NativeFunction)>>( + "Recorder__resume") + .asFunction)>(); + + /// from: public abstract void resume() + void resume() { + return _resume(reference).check(); + } + + static final _pause = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("Recorder__pause") + .asFunction)>(); + + /// from: public abstract void pause() + void pause() { + return _pause(reference).check(); + } + + static final _stop = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("Recorder__stop") + .asFunction)>(); + + /// from: public abstract void stop() + void stop() { + return _stop(reference).check(); + } +} + +class $RecorderType extends jni.JObjType { + const $RecorderType(); + + @override + String get signature => r"Lio/sentry/android/replay/Recorder;"; + + @override + Recorder fromRef(jni.JObjectPtr ref) => Recorder.fromRef(ref); + + @override + jni.JObjType get superType => const jni.JObjectType(); + + @override + final superCount = 1; + + @override + int get hashCode => ($RecorderType).hashCode; + + @override + bool operator ==(Object other) { + return other.runtimeType == ($RecorderType) && other is $RecorderType; + } +} + +/// from: io.sentry.android.replay.ScreenshotRecorderConfig +class ScreenshotRecorderConfig extends jni.JObject { + @override + late final jni.JObjType $type = type; + + ScreenshotRecorderConfig.fromRef( + jni.JObjectPtr ref, + ) : super.fromRef(ref); + + /// The type which includes information such as the signature of this class. + static const type = $ScreenshotRecorderConfigType(); + static final _get_Companion = + jniLookup>( + "get_ScreenshotRecorderConfig__Companion") + .asFunction(); + + /// from: static public final io.sentry.android.replay.ScreenshotRecorderConfig$Companion Companion + /// The returned object must be deleted after use, by calling the `delete` method. + static jni.JObject get Companion => + const jni.JObjectType().fromRef(_get_Companion().object); + + static final _ctor = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Int32, ffi.Int32, ffi.Float, ffi.Float, + ffi.Int32, ffi.Int32)>>("ScreenshotRecorderConfig__ctor") + .asFunction(); + + /// from: public void (int i, int i1, float f, float f1, int i2, int i3) + /// The returned object must be deleted after use, by calling the `delete` method. + factory ScreenshotRecorderConfig( + int i, + int i1, + double f, + double f1, + int i2, + int i3, + ) { + return ScreenshotRecorderConfig.fromRef(_ctor(i, i1, f, f1, i2, i3).object); + } + + static final _getRecordingWidth = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__getRecordingWidth") + .asFunction)>(); + + /// from: public final int getRecordingWidth() + int getRecordingWidth() { + return _getRecordingWidth(reference).integer; + } + + static final _getRecordingHeight = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__getRecordingHeight") + .asFunction)>(); + + /// from: public final int getRecordingHeight() + int getRecordingHeight() { + return _getRecordingHeight(reference).integer; + } + + static final _getScaleFactorX = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__getScaleFactorX") + .asFunction)>(); + + /// from: public final float getScaleFactorX() + double getScaleFactorX() { + return _getScaleFactorX(reference).float; + } + + static final _getScaleFactorY = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__getScaleFactorY") + .asFunction)>(); + + /// from: public final float getScaleFactorY() + double getScaleFactorY() { + return _getScaleFactorY(reference).float; + } + + static final _getFrameRate = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__getFrameRate") + .asFunction)>(); + + /// from: public final int getFrameRate() + int getFrameRate() { + return _getFrameRate(reference).integer; + } + + static final _getBitRate = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__getBitRate") + .asFunction)>(); + + /// from: public final int getBitRate() + int getBitRate() { + return _getBitRate(reference).integer; + } + + static final _component1 = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__component1") + .asFunction)>(); + + /// from: public final int component1() + int component1() { + return _component1(reference).integer; + } + + static final _component2 = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__component2") + .asFunction)>(); + + /// from: public final int component2() + int component2() { + return _component2(reference).integer; + } + + static final _component3 = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__component3") + .asFunction)>(); + + /// from: public final float component3() + double component3() { + return _component3(reference).float; + } + + static final _component4 = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__component4") + .asFunction)>(); + + /// from: public final float component4() + double component4() { + return _component4(reference).float; + } + + static final _component5 = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__component5") + .asFunction)>(); + + /// from: public final int component5() + int component5() { + return _component5(reference).integer; + } + + static final _component6 = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__component6") + .asFunction)>(); + + /// from: public final int component6() + int component6() { + return _component6(reference).integer; + } + + static final _copy = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, + ffi.Int32, + ffi.Int32, + ffi.Float, + ffi.Float, + ffi.Int32, + ffi.Int32)>>("ScreenshotRecorderConfig__copy") + .asFunction< + jni.JniResult Function( + ffi.Pointer, int, int, double, double, int, int)>(); + + /// from: public final io.sentry.android.replay.ScreenshotRecorderConfig copy(int i, int i1, float f, float f1, int i2, int i3) + /// The returned object must be deleted after use, by calling the `delete` method. + ScreenshotRecorderConfig copy( + int i, + int i1, + double f, + double f1, + int i2, + int i3, + ) { + return const $ScreenshotRecorderConfigType() + .fromRef(_copy(reference, i, i1, f, f1, i2, i3).object); + } + + static final _toString1 = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__toString1") + .asFunction)>(); + + /// from: public java.lang.String toString() + /// The returned object must be deleted after use, by calling the `delete` method. + jni.JString toString1() { + return const jni.JStringType().fromRef(_toString1(reference).object); + } + + static final _hashCode1 = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__hashCode1") + .asFunction)>(); + + /// from: public int hashCode() + int hashCode1() { + return _hashCode1(reference).integer; + } + + static final _equals1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("ScreenshotRecorderConfig__equals1") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public boolean equals(java.lang.Object object) + bool equals1( + jni.JObject object, + ) { + return _equals1(reference, object.reference).boolean; + } +} + +class $ScreenshotRecorderConfigType + extends jni.JObjType { + const $ScreenshotRecorderConfigType(); + + @override + String get signature => + r"Lio/sentry/android/replay/ScreenshotRecorderConfig;"; + + @override + ScreenshotRecorderConfig fromRef(jni.JObjectPtr ref) => + ScreenshotRecorderConfig.fromRef(ref); + + @override + jni.JObjType get superType => const jni.JObjectType(); + + @override + final superCount = 1; + + @override + int get hashCode => ($ScreenshotRecorderConfigType).hashCode; + + @override + bool operator ==(Object other) { + return other.runtimeType == ($ScreenshotRecorderConfigType) && + other is $ScreenshotRecorderConfigType; + } +} From a4e056aa47129bfbc827fd0255c110706bb5191f Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 17 Apr 2024 16:22:24 +0200 Subject: [PATCH 11/74] replay recorder binding jni code --- .../src/main/cpp/sentry_android_binding.c | 81 ++++++++++++++++ .../io/sentry/flutter/SentryFlutterReplay.kt | 7 ++ flutter/ffi-jni.yaml | 1 + flutter/lib/src/native/java/binding.dart | 95 +++++++++++++++++++ 4 files changed, 184 insertions(+) create mode 100644 flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt diff --git a/flutter/android/src/main/cpp/sentry_android_binding.c b/flutter/android/src/main/cpp/sentry_android_binding.c index ad564935c0..467affaebc 100644 --- a/flutter/android/src/main/cpp/sentry_android_binding.c +++ b/flutter/android/src/main/cpp/sentry_android_binding.c @@ -405,3 +405,84 @@ JniResult get_ScreenshotRecorderConfig__Companion() { _f_ScreenshotRecorderConfig__Companion); return to_global_ref_result(_result); } + +// io.sentry.flutter.SentryFlutterReplay +jclass _c_SentryFlutterReplay = NULL; + +jmethodID _m_SentryFlutterReplay__getRecorder = NULL; +FFI_PLUGIN_EXPORT +JniResult SentryFlutterReplay__getRecorder(jobject self_) { + load_env(); + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__getRecorder, + "getRecorder", "()Lio/sentry/android/replay/Recorder;"); + if (_m_SentryFlutterReplay__getRecorder == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_SentryFlutterReplay__getRecorder); + return to_global_ref_result(_result); +} + +jmethodID _m_SentryFlutterReplay__setRecorder = NULL; +FFI_PLUGIN_EXPORT +JniResult SentryFlutterReplay__setRecorder(jobject self_, jobject recorder) { + load_env(); + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__setRecorder, + "setRecorder", "(Lio/sentry/android/replay/Recorder;)V"); + if (_m_SentryFlutterReplay__setRecorder == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_SentryFlutterReplay__setRecorder, + recorder); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jfieldID _f_SentryFlutterReplay__INSTANCE = NULL; +FFI_PLUGIN_EXPORT +JniResult get_SentryFlutterReplay__INSTANCE() { + load_env(); + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__INSTANCE, + "INSTANCE", "Lio/sentry/flutter/SentryFlutterReplay;"); + jobject _result = (*jniEnv)->GetStaticObjectField( + jniEnv, _c_SentryFlutterReplay, _f_SentryFlutterReplay__INSTANCE); + return to_global_ref_result(_result); +} + +jfieldID _f_SentryFlutterReplay__recorder = NULL; +FFI_PLUGIN_EXPORT +JniResult get_SentryFlutterReplay__recorder() { + load_env(); + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__recorder, + "recorder", "Lio/sentry/android/replay/Recorder;"); + jobject _result = (*jniEnv)->GetStaticObjectField( + jniEnv, _c_SentryFlutterReplay, _f_SentryFlutterReplay__recorder); + return to_global_ref_result(_result); +} + +FFI_PLUGIN_EXPORT +JniResult set_SentryFlutterReplay__recorder(jobject value) { + load_env(); + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__recorder, + "recorder", "Lio/sentry/android/replay/Recorder;"); + (*jniEnv)->SetStaticObjectField(jniEnv, _c_SentryFlutterReplay, + _f_SentryFlutterReplay__recorder, value); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt new file mode 100644 index 0000000000..73811899a9 --- /dev/null +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt @@ -0,0 +1,7 @@ +package io.sentry.flutter + +import io.sentry.android.replay.Recorder + +object SentryFlutterReplay { + lateinit var recorder: Recorder +} diff --git a/flutter/ffi-jni.yaml b/flutter/ffi-jni.yaml index 97d24eb096..b168a65624 100644 --- a/flutter/ffi-jni.yaml +++ b/flutter/ffi-jni.yaml @@ -18,3 +18,4 @@ log_level: all classes: - io.sentry.android.replay.Recorder - io.sentry.android.replay.ScreenshotRecorderConfig + - io.sentry.flutter.SentryFlutterReplay diff --git a/flutter/lib/src/native/java/binding.dart b/flutter/lib/src/native/java/binding.dart index ef10f901f1..f1f71733df 100644 --- a/flutter/lib/src/native/java/binding.dart +++ b/flutter/lib/src/native/java/binding.dart @@ -372,3 +372,98 @@ class $ScreenshotRecorderConfigType other is $ScreenshotRecorderConfigType; } } + +/// from: io.sentry.flutter.SentryFlutterReplay +class SentryFlutterReplay extends jni.JObject { + @override + late final jni.JObjType $type = type; + + SentryFlutterReplay.fromRef( + jni.JObjectPtr ref, + ) : super.fromRef(ref); + + /// The type which includes information such as the signature of this class. + static const type = $SentryFlutterReplayType(); + static final _get_INSTANCE = + jniLookup>( + "get_SentryFlutterReplay__INSTANCE") + .asFunction(); + + /// from: static public final io.sentry.flutter.SentryFlutterReplay INSTANCE + /// The returned object must be deleted after use, by calling the `delete` method. + static SentryFlutterReplay get INSTANCE => + const $SentryFlutterReplayType().fromRef(_get_INSTANCE().object); + + static final _get_recorder = + jniLookup>( + "get_SentryFlutterReplay__recorder") + .asFunction(); + + static final _set_recorder = jniLookup< + ffi + .NativeFunction)>>( + "set_SentryFlutterReplay__recorder") + .asFunction)>(); + + /// from: static public io.sentry.android.replay.Recorder recorder + /// The returned object must be deleted after use, by calling the `delete` method. + static Recorder get recorder => + const $RecorderType().fromRef(_get_recorder().object); + + /// from: static public io.sentry.android.replay.Recorder recorder + /// The returned object must be deleted after use, by calling the `delete` method. + static set recorder(Recorder value) => _set_recorder(value.reference).check(); + + static final _getRecorder = jniLookup< + ffi + .NativeFunction)>>( + "SentryFlutterReplay__getRecorder") + .asFunction)>(); + + /// from: public final io.sentry.android.replay.Recorder getRecorder() + /// The returned object must be deleted after use, by calling the `delete` method. + Recorder getRecorder() { + return const $RecorderType().fromRef(_getRecorder(reference).object); + } + + static final _setRecorder = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("SentryFlutterReplay__setRecorder") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public final void setRecorder(io.sentry.android.replay.Recorder recorder) + void setRecorder( + Recorder recorder, + ) { + return _setRecorder(reference, recorder.reference).check(); + } +} + +class $SentryFlutterReplayType extends jni.JObjType { + const $SentryFlutterReplayType(); + + @override + String get signature => r"Lio/sentry/flutter/SentryFlutterReplay;"; + + @override + SentryFlutterReplay fromRef(jni.JObjectPtr ref) => + SentryFlutterReplay.fromRef(ref); + + @override + jni.JObjType get superType => const jni.JObjectType(); + + @override + final superCount = 1; + + @override + int get hashCode => ($SentryFlutterReplayType).hashCode; + + @override + bool operator ==(Object other) { + return other.runtimeType == ($SentryFlutterReplayType) && + other is $SentryFlutterReplayType; + } +} From 1cde8338041919b0c45b3b3545dfe4b0dd7f8779 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 18 Apr 2024 12:50:06 +0200 Subject: [PATCH 12/74] jni 0.6 --- flutter/android/build.gradle | 6 + flutter/android/src/main/cpp/dartjni.h | 64 ++++- .../src/main/cpp/sentry_android_binding.c | 58 ++++- flutter/ffi-jni.yaml | 3 + flutter/lib/src/native/java/binding.dart | 236 +++++++++++++++++- flutter/lib/src/replay/recorder_config.dart | 3 +- flutter/pubspec.yaml | 4 +- 7 files changed, 344 insertions(+), 30 deletions(-) diff --git a/flutter/android/build.gradle b/flutter/android/build.gradle index f4119f1e7b..42090f7d52 100644 --- a/flutter/android/build.gradle +++ b/flutter/android/build.gradle @@ -57,6 +57,12 @@ android { jvmTarget = JavaVersion.VERSION_1_8 languageVersion = "1.4" } + + externalNativeBuild { + cmake { + path "src/main/cpp/CMakeLists.txt" + } + } } dependencies { diff --git a/flutter/android/src/main/cpp/dartjni.h b/flutter/android/src/main/cpp/dartjni.h index c0713af53a..8f1dc7481c 100644 --- a/flutter/android/src/main/cpp/dartjni.h +++ b/flutter/android/src/main/cpp/dartjni.h @@ -45,6 +45,7 @@ #include typedef CRITICAL_SECTION MutexLock; +typedef CONDITION_VARIABLE ConditionVariable; static inline void init_lock(MutexLock* lock) { InitializeCriticalSection(lock); @@ -58,15 +59,32 @@ static inline void release_lock(MutexLock* lock) { LeaveCriticalSection(lock); } -static inline void _destroyLock(MutexLock* lock) { +static inline void destroy_lock(MutexLock* lock) { DeleteCriticalSection(lock); } -#elif defined __DARWIN__ || defined __LINUX__ || defined __ANDROID__ || \ +static inline void init_cond(ConditionVariable* cond) { + InitializeConditionVariable(cond); +} + +static inline void signal_cond(ConditionVariable* cond) { + WakeConditionVariable(cond); +} + +static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { + SleepConditionVariableCS(cond, lock, INFINITE); +} + +static inline void destroy_cond(ConditionVariable* cond) { + // Not available. +} + +#elif defined __APPLE__ || defined __LINUX__ || defined __ANDROID__ || \ defined __GNUC__ #include typedef pthread_mutex_t MutexLock; +typedef pthread_cond_t ConditionVariable; static inline void init_lock(MutexLock* lock) { pthread_mutex_init(lock, NULL); @@ -80,16 +98,39 @@ static inline void release_lock(MutexLock* lock) { pthread_mutex_unlock(lock); } -static inline void _destroyLock(MutexLock* lock) { +static inline void destroy_lock(MutexLock* lock) { pthread_mutex_destroy(lock); } +static inline void init_cond(ConditionVariable* cond) { + pthread_cond_init(cond, NULL); +} + +static inline void signal_cond(ConditionVariable* cond) { + pthread_cond_signal(cond); +} + +static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { + pthread_cond_wait(cond, lock); +} + +static inline void destroy_cond(ConditionVariable* cond) { + pthread_cond_destroy(cond); +} + #else -#error "No locking support; Possibly unsupported platform" +#error "No locking/condition variable support; Possibly unsupported platform" #endif +typedef struct CallbackResult { + MutexLock lock; + ConditionVariable cond; + int ready; + jobject object; +} CallbackResult; + typedef struct JniLocks { MutexLock classLoadingLock; MutexLock methodLoadingLock; @@ -369,10 +410,15 @@ static inline JniResult to_global_ref_result(jobject ref) { FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); -JNIEXPORT void JNICALL -Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env, - jobject thiz, - jlong port, - jobject result); +FFI_PLUGIN_EXPORT +JniResult DartException__ctor(jstring message); + FFI_PLUGIN_EXPORT JniResult PortContinuation__ctor(int64_t j); + +FFI_PLUGIN_EXPORT +JniResult PortProxy__newInstance(jobject binaryName, + int64_t port, + int64_t functionPtr); + +FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); diff --git a/flutter/android/src/main/cpp/sentry_android_binding.c b/flutter/android/src/main/cpp/sentry_android_binding.c index 467affaebc..0401f35337 100644 --- a/flutter/android/src/main/cpp/sentry_android_binding.c +++ b/flutter/android/src/main/cpp/sentry_android_binding.c @@ -76,12 +76,60 @@ JniResult Recorder__stop(jobject self_) { return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } +// io.sentry.android.replay.ScreenshotRecorderConfig$Companion +jclass _c_ScreenshotRecorderConfig_Companion = NULL; + +jmethodID _m_ScreenshotRecorderConfig_Companion__from = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig_Companion__from( + jobject self_, + jobject context, + jobject sentryReplayOptions) { + load_env(); + load_class_global_ref( + &_c_ScreenshotRecorderConfig_Companion, + "io/sentry/android/replay/ScreenshotRecorderConfig$Companion"); + if (_c_ScreenshotRecorderConfig_Companion == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig_Companion, + &_m_ScreenshotRecorderConfig_Companion__from, "from", + "(Landroid/content/Context;Lio/sentry/SentryReplayOptions;)Lio/" + "sentry/android/replay/ScreenshotRecorderConfig;"); + if (_m_ScreenshotRecorderConfig_Companion__from == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig_Companion__from, context, + sentryReplayOptions); + return to_global_ref_result(_result); +} + +jmethodID _m_ScreenshotRecorderConfig_Companion__new0 = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderConfig_Companion__new0( + jobject defaultConstructorMarker) { + load_env(); + load_class_global_ref( + &_c_ScreenshotRecorderConfig_Companion, + "io/sentry/android/replay/ScreenshotRecorderConfig$Companion"); + if (_c_ScreenshotRecorderConfig_Companion == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderConfig_Companion, + &_m_ScreenshotRecorderConfig_Companion__new0, "", + "(Lkotlin/jvm/internal/DefaultConstructorMarker;)V"); + if (_m_ScreenshotRecorderConfig_Companion__new0 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->NewObject( + jniEnv, _c_ScreenshotRecorderConfig_Companion, + _m_ScreenshotRecorderConfig_Companion__new0, defaultConstructorMarker); + return to_global_ref_result(_result); +} + // io.sentry.android.replay.ScreenshotRecorderConfig jclass _c_ScreenshotRecorderConfig = NULL; -jmethodID _m_ScreenshotRecorderConfig__ctor = NULL; +jmethodID _m_ScreenshotRecorderConfig__new0 = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__ctor(int32_t i, +JniResult ScreenshotRecorderConfig__new0(int32_t i, int32_t i1, float f, float f1, @@ -92,12 +140,12 @@ JniResult ScreenshotRecorderConfig__ctor(int32_t i, "io/sentry/android/replay/ScreenshotRecorderConfig"); if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, &_m_ScreenshotRecorderConfig__ctor, + load_method(_c_ScreenshotRecorderConfig, &_m_ScreenshotRecorderConfig__new0, "", "(IIFFII)V"); - if (_m_ScreenshotRecorderConfig__ctor == NULL) + if (_m_ScreenshotRecorderConfig__new0 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; jobject _result = (*jniEnv)->NewObject(jniEnv, _c_ScreenshotRecorderConfig, - _m_ScreenshotRecorderConfig__ctor, i, + _m_ScreenshotRecorderConfig__new0, i, i1, f, f1, i2, i3); return to_global_ref_result(_result); } diff --git a/flutter/ffi-jni.yaml b/flutter/ffi-jni.yaml index b168a65624..55c488eea1 100644 --- a/flutter/ffi-jni.yaml +++ b/flutter/ffi-jni.yaml @@ -19,3 +19,6 @@ classes: - io.sentry.android.replay.Recorder - io.sentry.android.replay.ScreenshotRecorderConfig - io.sentry.flutter.SentryFlutterReplay + +enable_experiment: + - interface_implementation diff --git a/flutter/lib/src/native/java/binding.dart b/flutter/lib/src/native/java/binding.dart index f1f71733df..c077d145cc 100644 --- a/flutter/lib/src/native/java/binding.dart +++ b/flutter/lib/src/native/java/binding.dart @@ -12,6 +12,7 @@ // ignore_for_file: unused_element // ignore_for_file: unused_field // ignore_for_file: unused_import +// ignore_for_file: unused_local_variable // ignore_for_file: unused_shown_name import "dart:isolate" show ReceivePort; @@ -80,6 +81,139 @@ class Recorder extends jni.JObject { void stop() { return _stop(reference).check(); } + + /// Maps a specific port to the implemented interface. + static final Map _$impls = {}; + ReceivePort? _$p; + + static jni.JObjectPtr _$invoke( + int port, + jni.JObjectPtr descriptor, + jni.JObjectPtr args, + ) { + return _$invokeMethod( + port, + $MethodInvocation.fromAddresses( + 0, + descriptor.address, + args.address, + ), + ); + } + + static final ffi.Pointer< + ffi.NativeFunction< + jni.JObjectPtr Function( + ffi.Uint64, jni.JObjectPtr, jni.JObjectPtr)>> + _$invokePointer = ffi.Pointer.fromFunction(_$invoke); + + static ffi.Pointer _$invokeMethod( + int $p, + $MethodInvocation $i, + ) { + try { + final $d = $i.methodDescriptor.toDartString(releaseOriginal: true); + final $a = $i.args; + if ($d == + r"start(Lio/sentry/android/replay/ScreenshotRecorderConfig;)V") { + _$impls[$p]!.start( + $a[0].castTo(const $ScreenshotRecorderConfigType(), + releaseOriginal: true), + ); + return jni.nullptr; + } + if ($d == r"resume()V") { + _$impls[$p]!.resume(); + return jni.nullptr; + } + if ($d == r"pause()V") { + _$impls[$p]!.pause(); + return jni.nullptr; + } + if ($d == r"stop()V") { + _$impls[$p]!.stop(); + return jni.nullptr; + } + } catch (e) { + return ProtectedJniExtensions.newDartException(e.toString()); + } + return jni.nullptr; + } + + factory Recorder.implement( + $RecorderImpl $impl, + ) { + final $p = ReceivePort(); + final $x = Recorder.fromRef( + ProtectedJniExtensions.newPortProxy( + r"io.sentry.android.replay.Recorder", + $p, + _$invokePointer, + ), + ).._$p = $p; + final $a = $p.sendPort.nativePort; + _$impls[$a] = $impl; + $p.listen(($m) { + if ($m == null) { + _$impls.remove($p.sendPort.nativePort); + $p.close(); + return; + } + final $i = $MethodInvocation.fromMessage($m); + final $r = _$invokeMethod($p.sendPort.nativePort, $i); + ProtectedJniExtensions.returnResult($i.result, $r); + }); + return $x; + } +} + +abstract class $RecorderImpl { + factory $RecorderImpl({ + required void Function(ScreenshotRecorderConfig screenshotRecorderConfig) + start, + required void Function() resume, + required void Function() pause, + required void Function() stop, + }) = _$RecorderImpl; + + void start(ScreenshotRecorderConfig screenshotRecorderConfig); + void resume(); + void pause(); + void stop(); +} + +class _$RecorderImpl implements $RecorderImpl { + _$RecorderImpl({ + required void Function(ScreenshotRecorderConfig screenshotRecorderConfig) + start, + required void Function() resume, + required void Function() pause, + required void Function() stop, + }) : _start = start, + _resume = resume, + _pause = pause, + _stop = stop; + + final void Function(ScreenshotRecorderConfig screenshotRecorderConfig) _start; + final void Function() _resume; + final void Function() _pause; + final void Function() _stop; + + void start(ScreenshotRecorderConfig screenshotRecorderConfig) { + return _start(screenshotRecorderConfig); + } + + void resume() { + return _resume(); + } + + void pause() { + return _pause(); + } + + void stop() { + return _stop(); + } } class $RecorderType extends jni.JObjType { @@ -106,6 +240,81 @@ class $RecorderType extends jni.JObjType { } } +/// from: io.sentry.android.replay.ScreenshotRecorderConfig$Companion +class ScreenshotRecorderConfig_Companion extends jni.JObject { + @override + late final jni.JObjType $type = type; + + ScreenshotRecorderConfig_Companion.fromRef( + jni.JObjectPtr ref, + ) : super.fromRef(ref); + + /// The type which includes information such as the signature of this class. + static const type = $ScreenshotRecorderConfig_CompanionType(); + static final _from = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer, ffi.Pointer)>>( + "ScreenshotRecorderConfig_Companion__from") + .asFunction< + jni.JniResult Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + /// from: public final io.sentry.android.replay.ScreenshotRecorderConfig from(android.content.Context context, io.sentry.SentryReplayOptions sentryReplayOptions) + /// The returned object must be released after use, by calling the [release] method. + ScreenshotRecorderConfig from( + jni.JObject context, + jni.JObject sentryReplayOptions, + ) { + return const $ScreenshotRecorderConfigType().fromRef( + _from(reference, context.reference, sentryReplayOptions.reference) + .object); + } + + static final _new0 = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig_Companion__new0") + .asFunction)>(); + + /// from: public void (kotlin.jvm.internal.DefaultConstructorMarker defaultConstructorMarker) + /// The returned object must be released after use, by calling the [release] method. + factory ScreenshotRecorderConfig_Companion( + jni.JObject defaultConstructorMarker, + ) { + return ScreenshotRecorderConfig_Companion.fromRef( + _new0(defaultConstructorMarker.reference).object); + } +} + +class $ScreenshotRecorderConfig_CompanionType + extends jni.JObjType { + const $ScreenshotRecorderConfig_CompanionType(); + + @override + String get signature => + r"Lio/sentry/android/replay/ScreenshotRecorderConfig$Companion;"; + + @override + ScreenshotRecorderConfig_Companion fromRef(jni.JObjectPtr ref) => + ScreenshotRecorderConfig_Companion.fromRef(ref); + + @override + jni.JObjType get superType => const jni.JObjectType(); + + @override + final superCount = 1; + + @override + int get hashCode => ($ScreenshotRecorderConfig_CompanionType).hashCode; + + @override + bool operator ==(Object other) { + return other.runtimeType == ($ScreenshotRecorderConfig_CompanionType) && + other is $ScreenshotRecorderConfig_CompanionType; + } +} + /// from: io.sentry.android.replay.ScreenshotRecorderConfig class ScreenshotRecorderConfig extends jni.JObject { @override @@ -123,18 +332,19 @@ class ScreenshotRecorderConfig extends jni.JObject { .asFunction(); /// from: static public final io.sentry.android.replay.ScreenshotRecorderConfig$Companion Companion - /// The returned object must be deleted after use, by calling the `delete` method. - static jni.JObject get Companion => - const jni.JObjectType().fromRef(_get_Companion().object); + /// The returned object must be released after use, by calling the [release] method. + static ScreenshotRecorderConfig_Companion get Companion => + const $ScreenshotRecorderConfig_CompanionType() + .fromRef(_get_Companion().object); - static final _ctor = jniLookup< + static final _new0 = jniLookup< ffi.NativeFunction< jni.JniResult Function(ffi.Int32, ffi.Int32, ffi.Float, ffi.Float, - ffi.Int32, ffi.Int32)>>("ScreenshotRecorderConfig__ctor") + ffi.Int32, ffi.Int32)>>("ScreenshotRecorderConfig__new0") .asFunction(); /// from: public void (int i, int i1, float f, float f1, int i2, int i3) - /// The returned object must be deleted after use, by calling the `delete` method. + /// The returned object must be released after use, by calling the [release] method. factory ScreenshotRecorderConfig( int i, int i1, @@ -143,7 +353,7 @@ class ScreenshotRecorderConfig extends jni.JObject { int i2, int i3, ) { - return ScreenshotRecorderConfig.fromRef(_ctor(i, i1, f, f1, i2, i3).object); + return ScreenshotRecorderConfig.fromRef(_new0(i, i1, f, f1, i2, i3).object); } static final _getRecordingWidth = jniLookup< @@ -293,7 +503,7 @@ class ScreenshotRecorderConfig extends jni.JObject { ffi.Pointer, int, int, double, double, int, int)>(); /// from: public final io.sentry.android.replay.ScreenshotRecorderConfig copy(int i, int i1, float f, float f1, int i2, int i3) - /// The returned object must be deleted after use, by calling the `delete` method. + /// The returned object must be released after use, by calling the [release] method. ScreenshotRecorderConfig copy( int i, int i1, @@ -313,7 +523,7 @@ class ScreenshotRecorderConfig extends jni.JObject { .asFunction)>(); /// from: public java.lang.String toString() - /// The returned object must be deleted after use, by calling the `delete` method. + /// The returned object must be released after use, by calling the [release] method. jni.JString toString1() { return const jni.JStringType().fromRef(_toString1(reference).object); } @@ -390,7 +600,7 @@ class SentryFlutterReplay extends jni.JObject { .asFunction(); /// from: static public final io.sentry.flutter.SentryFlutterReplay INSTANCE - /// The returned object must be deleted after use, by calling the `delete` method. + /// The returned object must be released after use, by calling the [release] method. static SentryFlutterReplay get INSTANCE => const $SentryFlutterReplayType().fromRef(_get_INSTANCE().object); @@ -406,12 +616,12 @@ class SentryFlutterReplay extends jni.JObject { .asFunction)>(); /// from: static public io.sentry.android.replay.Recorder recorder - /// The returned object must be deleted after use, by calling the `delete` method. + /// The returned object must be released after use, by calling the [release] method. static Recorder get recorder => const $RecorderType().fromRef(_get_recorder().object); /// from: static public io.sentry.android.replay.Recorder recorder - /// The returned object must be deleted after use, by calling the `delete` method. + /// The returned object must be released after use, by calling the [release] method. static set recorder(Recorder value) => _set_recorder(value.reference).check(); static final _getRecorder = jniLookup< @@ -421,7 +631,7 @@ class SentryFlutterReplay extends jni.JObject { .asFunction)>(); /// from: public final io.sentry.android.replay.Recorder getRecorder() - /// The returned object must be deleted after use, by calling the `delete` method. + /// The returned object must be released after use, by calling the [release] method. Recorder getRecorder() { return const $RecorderType().fromRef(_getRecorder(reference).object); } diff --git a/flutter/lib/src/replay/recorder_config.dart b/flutter/lib/src/replay/recorder_config.dart index 10959eb0d8..1ac7de7995 100644 --- a/flutter/lib/src/replay/recorder_config.dart +++ b/flutter/lib/src/replay/recorder_config.dart @@ -7,5 +7,6 @@ class ScreenshotRecorderConfig { final int frameRate; final int bitRate; - ScreenshotRecorderConfig(this.width, this.height, this.frameRate, this.bitRate) + ScreenshotRecorderConfig( + this.width, this.height, this.frameRate, this.bitRate); } diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 5b5a8de235..ade4a8ec6b 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: package_info_plus: '>=1.0.0 <7.0.0' meta: ^1.3.0 ffi: ^2.0.0 - jni: ^0.5.0 + jni: ^0.6.0 dev_dependencies: build_runner: ^2.4.2 @@ -32,7 +32,7 @@ dev_dependencies: remove_from_coverage: ^2.0.0 flutter_localizations: sdk: flutter - jnigen: ^0.5.0 + jnigen: ^0.6.0 flutter: plugin: From 010f57503583b0eceb966afdf3b2002d1a63da23 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 18 Apr 2024 15:18:30 +0200 Subject: [PATCH 13/74] wip: android jni replay --- .../io/sentry/flutter/SentryFlutterPlugin.kt | 29 +++++++++++ .../io/sentry/flutter/SentryFlutterReplay.kt | 7 +++ flutter/lib/src/native/factory_real.dart | 3 ++ .../native/java/android_replay_recorder.dart | 48 +++++++++++++++++++ .../src/native/java/sentry_native_java.dart | 14 ++++++ flutter/lib/src/sentry_replay_options.dart | 4 ++ flutter/pubspec.yaml | 1 + 7 files changed, 106 insertions(+) create mode 100644 flutter/lib/src/native/java/android_replay_recorder.dart create mode 100644 flutter/lib/src/native/java/sentry_native_java.dart diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index ea9ab9d17e..7224535df4 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -24,10 +24,12 @@ import io.sentry.android.core.LoadClass import io.sentry.android.core.SentryAndroid import io.sentry.android.core.SentryAndroidOptions import io.sentry.android.core.performance.AppStartMetrics +import io.sentry.android.replay.ReplayIntegration import io.sentry.protocol.DebugImage import io.sentry.protocol.SdkVersion import io.sentry.protocol.SentryId import io.sentry.protocol.User +import io.sentry.transport.CurrentDateProvider import java.io.File import java.lang.ref.WeakReference import java.util.Locale @@ -131,6 +133,33 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } options.beforeSend = BeforeSendCallbackImpl(options.sdkVersion) + + // Replace the default ReplayIntegration with a custom one that uses a Flutter-specific recorder. + options.integrations.removeAll { it is ReplayIntegration } + val recorder = SentryFlutterReplay.recorder + if (recorder != null && options.cacheDirPath != null) { + var replay = ReplayIntegration( + context, + dateProvider = CurrentDateProvider.getInstance(), + recorderProvider = { recorder }, + recorderConfigProvider = null, // TODO implement in dart + replayCacheProvider = null + // TODO we only do this to have a cache dir path where to save stuff. + // replayCacheProvider = { replayId -> + // SentryFlutterReplay.replayCacheDir = File(options.cacheDirPath!!, "replay_$replayId") + // ReplayCache(options, replayId, recorderConfig) + // }, + ) + + // FIXME temporary + SentryFlutterReplay.cacheDir = File(options.cacheDirPath!!, "replay_0") + + options.addIntegration(replay) + options.setReplayController(replay) + SentryFlutterReplay.callback = replay + } else { + options.setReplayController(null) + } } result.success("") } diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt index 73811899a9..96fff0c018 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt @@ -3,5 +3,12 @@ package io.sentry.flutter import io.sentry.android.replay.Recorder object SentryFlutterReplay { + // Set by the Flutter side, read during SentryAndroid.init() lateinit var recorder: Recorder + + // Set by SentryAndroid.init(), read by the Flutter side in recorder.start() + lateinit var cacheDir: String + + // Set by SentryAndroid.init(), read by the Flutter side in recorder.start() + lateinit var callback: ScreenshotRecorderCallback } diff --git a/flutter/lib/src/native/factory_real.dart b/flutter/lib/src/native/factory_real.dart index 3c918b7be7..d5ee4f5cca 100644 --- a/flutter/lib/src/native/factory_real.dart +++ b/flutter/lib/src/native/factory_real.dart @@ -2,12 +2,15 @@ import 'package:flutter/services.dart'; import '../../sentry_flutter.dart'; import 'cocoa/sentry_native_cocoa.dart'; +import 'java/sentry_native_java.dart'; import 'sentry_native_binding.dart'; import 'sentry_native_channel.dart'; SentryNativeBinding createBinding(PlatformChecker pc, MethodChannel channel) { if (pc.platform.isIOS || pc.platform.isMacOS) { return SentryNativeCocoa(channel); + } else if (pc.platform.isAndroid) { + return SentryNativeJava(channel); } else { return SentryNativeChannel(channel); } diff --git a/flutter/lib/src/native/java/android_replay_recorder.dart b/flutter/lib/src/native/java/android_replay_recorder.dart new file mode 100644 index 0000000000..57d0260fe3 --- /dev/null +++ b/flutter/lib/src/native/java/android_replay_recorder.dart @@ -0,0 +1,48 @@ +import 'dart:io'; +import 'dart:ui'; + +import 'package:meta/meta.dart'; +import 'package:path/path.dart' as path; + +import '../../replay/recorder.dart'; +import '../../replay/recorder_config.dart'; +import 'binding.dart' as java; + +@internal +class AndroidReplayRecorder implements java.$RecorderImpl { + late ScreenshotRecorder _recorder; + + AndroidReplayRecorder._(); + + static java.Recorder create() => + java.Recorder.implement(AndroidReplayRecorder._()); + + @override + void pause() => _recorder.stop(); + + @override + void resume() => _recorder.start(); + + @override + void start(java.ScreenshotRecorderConfig config) { + var cacheDir = java.SentryFlutterReplay.cacheDir; + _recorder = ScreenshotRecorder( + ScreenshotRecorderConfig( + config.getRecordingWidth(), + config.getRecordingHeight(), + config.getFrameRate(), + config.getBitRate(), + ), (image) async { + var imageData = await image.toByteData(format: ImageByteFormat.png); + if (imageData != null) { + var filePath = + path.join(cacheDir, "${DateTime.now().millisecondsSinceEpoch}.png"); + await File(filePath).writeAsBytes(imageData.buffer.asUint8List()); + } + }); + _recorder.start(); + } + + @override + void stop() => _recorder.stop(); +} diff --git a/flutter/lib/src/native/java/sentry_native_java.dart b/flutter/lib/src/native/java/sentry_native_java.dart new file mode 100644 index 0000000000..84ae6bee0d --- /dev/null +++ b/flutter/lib/src/native/java/sentry_native_java.dart @@ -0,0 +1,14 @@ +import 'package:jni/jni.dart'; +import 'package:meta/meta.dart'; + +import '../sentry_native_channel.dart'; +import 'android_replay_recorder.dart'; +import 'binding.dart' as java; + +@internal +class SentryNativeJava extends SentryNativeChannel { + SentryNativeJava(super.channel) { + Jni.initDLApi(); + java.SentryFlutterReplay.recorder = AndroidReplayRecorder.create(); + } +} diff --git a/flutter/lib/src/sentry_replay_options.dart b/flutter/lib/src/sentry_replay_options.dart index 198fa895bf..93c7425713 100644 --- a/flutter/lib/src/sentry_replay_options.dart +++ b/flutter/lib/src/sentry_replay_options.dart @@ -67,4 +67,8 @@ class SentryReplayOptions { /// The maximum duration of a full session replay, defaults to 1h. @internal Duration sessionDuration = Duration(hours: 1); + + @internal + bool get isEnabled => + ((sessionSampleRate ?? 0) > 0) || ((errorSampleRate ?? 0) > 0); } diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index ade4a8ec6b..23fe33b315 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -20,6 +20,7 @@ dependencies: meta: ^1.3.0 ffi: ^2.0.0 jni: ^0.6.0 + path: ^1.8.0 dev_dependencies: build_runner: ^2.4.2 From 4c4f13289db1dd048e59f4f52b9dce7cb23b453c Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 18 Apr 2024 15:46:46 +0200 Subject: [PATCH 14/74] replay binding --- .../src/main/cpp/sentry_android_binding.c | 1065 +++++++++++++++++ .../io/sentry/flutter/SentryFlutterPlugin.kt | 2 +- .../io/sentry/flutter/SentryFlutterReplay.kt | 1 + flutter/ffi-jni.yaml | 2 + .../native/java/android_replay_recorder.dart | 26 +- flutter/lib/src/native/java/binding.dart | 1038 ++++++++++++++++ 6 files changed, 2129 insertions(+), 5 deletions(-) diff --git a/flutter/android/src/main/cpp/sentry_android_binding.c b/flutter/android/src/main/cpp/sentry_android_binding.c index 0401f35337..ec9ac12159 100644 --- a/flutter/android/src/main/cpp/sentry_android_binding.c +++ b/flutter/android/src/main/cpp/sentry_android_binding.c @@ -454,6 +454,50 @@ JniResult get_ScreenshotRecorderConfig__Companion() { return to_global_ref_result(_result); } +// io.sentry.android.replay.ScreenshotRecorderCallback +jclass _c_ScreenshotRecorderCallback = NULL; + +jmethodID _m_ScreenshotRecorderCallback__onScreenshotRecorded = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderCallback__onScreenshotRecorded(jobject self_, + jobject bitmap) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderCallback, + "io/sentry/android/replay/ScreenshotRecorderCallback"); + if (_c_ScreenshotRecorderCallback == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderCallback, + &_m_ScreenshotRecorderCallback__onScreenshotRecorded, + "onScreenshotRecorded", "(Landroid/graphics/Bitmap;)V"); + if (_m_ScreenshotRecorderCallback__onScreenshotRecorded == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, + _m_ScreenshotRecorderCallback__onScreenshotRecorded, + bitmap); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jmethodID _m_ScreenshotRecorderCallback__onScreenshotRecorded1 = NULL; +FFI_PLUGIN_EXPORT +JniResult ScreenshotRecorderCallback__onScreenshotRecorded1(jobject self_, + jobject file, + int64_t j) { + load_env(); + load_class_global_ref(&_c_ScreenshotRecorderCallback, + "io/sentry/android/replay/ScreenshotRecorderCallback"); + if (_c_ScreenshotRecorderCallback == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ScreenshotRecorderCallback, + &_m_ScreenshotRecorderCallback__onScreenshotRecorded1, + "onScreenshotRecorded", "(Ljava/io/File;J)V"); + if (_m_ScreenshotRecorderCallback__onScreenshotRecorded1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod( + jniEnv, self_, _m_ScreenshotRecorderCallback__onScreenshotRecorded1, file, + j); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + // io.sentry.flutter.SentryFlutterReplay jclass _c_SentryFlutterReplay = NULL; @@ -491,6 +535,77 @@ JniResult SentryFlutterReplay__setRecorder(jobject self_, jobject recorder) { return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } +jmethodID _m_SentryFlutterReplay__getCacheDir = NULL; +FFI_PLUGIN_EXPORT +JniResult SentryFlutterReplay__getCacheDir(jobject self_) { + load_env(); + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__getCacheDir, + "getCacheDir", "()Ljava/lang/String;"); + if (_m_SentryFlutterReplay__getCacheDir == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_SentryFlutterReplay__getCacheDir); + return to_global_ref_result(_result); +} + +jmethodID _m_SentryFlutterReplay__setCacheDir = NULL; +FFI_PLUGIN_EXPORT +JniResult SentryFlutterReplay__setCacheDir(jobject self_, jobject string) { + load_env(); + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__setCacheDir, + "setCacheDir", "(Ljava/lang/String;)V"); + if (_m_SentryFlutterReplay__setCacheDir == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_SentryFlutterReplay__setCacheDir, + string); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jmethodID _m_SentryFlutterReplay__getCallback = NULL; +FFI_PLUGIN_EXPORT +JniResult SentryFlutterReplay__getCallback(jobject self_) { + load_env(); + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__getCallback, + "getCallback", + "()Lio/sentry/android/replay/ScreenshotRecorderCallback;"); + if (_m_SentryFlutterReplay__getCallback == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_SentryFlutterReplay__getCallback); + return to_global_ref_result(_result); +} + +jmethodID _m_SentryFlutterReplay__setCallback = NULL; +FFI_PLUGIN_EXPORT +JniResult SentryFlutterReplay__setCallback(jobject self_, + jobject screenshotRecorderCallback) { + load_env(); + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__setCallback, + "setCallback", + "(Lio/sentry/android/replay/ScreenshotRecorderCallback;)V"); + if (_m_SentryFlutterReplay__setCallback == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_SentryFlutterReplay__setCallback, + screenshotRecorderCallback); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + jfieldID _f_SentryFlutterReplay__INSTANCE = NULL; FFI_PLUGIN_EXPORT JniResult get_SentryFlutterReplay__INSTANCE() { @@ -534,3 +649,953 @@ JniResult set_SentryFlutterReplay__recorder(jobject value) { _f_SentryFlutterReplay__recorder, value); return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } + +jfieldID _f_SentryFlutterReplay__cacheDir = NULL; +FFI_PLUGIN_EXPORT +JniResult get_SentryFlutterReplay__cacheDir() { + load_env(); + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__cacheDir, + "cacheDir", "Ljava/lang/String;"); + jobject _result = (*jniEnv)->GetStaticObjectField( + jniEnv, _c_SentryFlutterReplay, _f_SentryFlutterReplay__cacheDir); + return to_global_ref_result(_result); +} + +FFI_PLUGIN_EXPORT +JniResult set_SentryFlutterReplay__cacheDir(jobject value) { + load_env(); + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__cacheDir, + "cacheDir", "Ljava/lang/String;"); + (*jniEnv)->SetStaticObjectField(jniEnv, _c_SentryFlutterReplay, + _f_SentryFlutterReplay__cacheDir, value); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jfieldID _f_SentryFlutterReplay__callback = NULL; +FFI_PLUGIN_EXPORT +JniResult get_SentryFlutterReplay__callback() { + load_env(); + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__callback, + "callback", + "Lio/sentry/android/replay/ScreenshotRecorderCallback;"); + jobject _result = (*jniEnv)->GetStaticObjectField( + jniEnv, _c_SentryFlutterReplay, _f_SentryFlutterReplay__callback); + return to_global_ref_result(_result); +} + +FFI_PLUGIN_EXPORT +JniResult set_SentryFlutterReplay__callback(jobject value) { + load_env(); + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__callback, + "callback", + "Lio/sentry/android/replay/ScreenshotRecorderCallback;"); + (*jniEnv)->SetStaticObjectField(jniEnv, _c_SentryFlutterReplay, + _f_SentryFlutterReplay__callback, value); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +// java.io.File +jclass _c_File = NULL; + +jmethodID _m_File__new0 = NULL; +FFI_PLUGIN_EXPORT +JniResult File__new0(jobject string) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__new0, "", "(Ljava/lang/String;)V"); + if (_m_File__new0 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->NewObject(jniEnv, _c_File, _m_File__new0, string); + return to_global_ref_result(_result); +} + +jmethodID _m_File__new1 = NULL; +FFI_PLUGIN_EXPORT +JniResult File__new1(jobject string, jobject string1) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__new1, "", + "(Ljava/lang/String;Ljava/lang/String;)V"); + if (_m_File__new1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->NewObject(jniEnv, _c_File, _m_File__new1, string, string1); + return to_global_ref_result(_result); +} + +jmethodID _m_File__new2 = NULL; +FFI_PLUGIN_EXPORT +JniResult File__new2(jobject file, jobject string) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__new2, "", + "(Ljava/io/File;Ljava/lang/String;)V"); + if (_m_File__new2 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->NewObject(jniEnv, _c_File, _m_File__new2, file, string); + return to_global_ref_result(_result); +} + +jmethodID _m_File__new3 = NULL; +FFI_PLUGIN_EXPORT +JniResult File__new3(jobject uRI) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__new3, "", "(Ljava/net/URI;)V"); + if (_m_File__new3 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->NewObject(jniEnv, _c_File, _m_File__new3, uRI); + return to_global_ref_result(_result); +} + +jmethodID _m_File__getName = NULL; +FFI_PLUGIN_EXPORT +JniResult File__getName(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__getName, "getName", "()Ljava/lang/String;"); + if (_m_File__getName == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getName); + return to_global_ref_result(_result); +} + +jmethodID _m_File__getParent = NULL; +FFI_PLUGIN_EXPORT +JniResult File__getParent(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__getParent, "getParent", + "()Ljava/lang/String;"); + if (_m_File__getParent == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getParent); + return to_global_ref_result(_result); +} + +jmethodID _m_File__getParentFile = NULL; +FFI_PLUGIN_EXPORT +JniResult File__getParentFile(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__getParentFile, "getParentFile", + "()Ljava/io/File;"); + if (_m_File__getParentFile == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getParentFile); + return to_global_ref_result(_result); +} + +jmethodID _m_File__getPath = NULL; +FFI_PLUGIN_EXPORT +JniResult File__getPath(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__getPath, "getPath", "()Ljava/lang/String;"); + if (_m_File__getPath == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getPath); + return to_global_ref_result(_result); +} + +jmethodID _m_File__isAbsolute = NULL; +FFI_PLUGIN_EXPORT +JniResult File__isAbsolute(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__isAbsolute, "isAbsolute", "()Z"); + if (_m_File__isAbsolute == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__isAbsolute); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__getAbsolutePath = NULL; +FFI_PLUGIN_EXPORT +JniResult File__getAbsolutePath(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__getAbsolutePath, "getAbsolutePath", + "()Ljava/lang/String;"); + if (_m_File__getAbsolutePath == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getAbsolutePath); + return to_global_ref_result(_result); +} + +jmethodID _m_File__getAbsoluteFile = NULL; +FFI_PLUGIN_EXPORT +JniResult File__getAbsoluteFile(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__getAbsoluteFile, "getAbsoluteFile", + "()Ljava/io/File;"); + if (_m_File__getAbsoluteFile == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getAbsoluteFile); + return to_global_ref_result(_result); +} + +jmethodID _m_File__getCanonicalPath = NULL; +FFI_PLUGIN_EXPORT +JniResult File__getCanonicalPath(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__getCanonicalPath, "getCanonicalPath", + "()Ljava/lang/String;"); + if (_m_File__getCanonicalPath == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getCanonicalPath); + return to_global_ref_result(_result); +} + +jmethodID _m_File__getCanonicalFile = NULL; +FFI_PLUGIN_EXPORT +JniResult File__getCanonicalFile(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__getCanonicalFile, "getCanonicalFile", + "()Ljava/io/File;"); + if (_m_File__getCanonicalFile == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getCanonicalFile); + return to_global_ref_result(_result); +} + +jmethodID _m_File__toURL = NULL; +FFI_PLUGIN_EXPORT +JniResult File__toURL(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__toURL, "toURL", "()Ljava/net/URL;"); + if (_m_File__toURL == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__toURL); + return to_global_ref_result(_result); +} + +jmethodID _m_File__toURI = NULL; +FFI_PLUGIN_EXPORT +JniResult File__toURI(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__toURI, "toURI", "()Ljava/net/URI;"); + if (_m_File__toURI == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__toURI); + return to_global_ref_result(_result); +} + +jmethodID _m_File__canRead = NULL; +FFI_PLUGIN_EXPORT +JniResult File__canRead(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__canRead, "canRead", "()Z"); + if (_m_File__canRead == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__canRead); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__canWrite = NULL; +FFI_PLUGIN_EXPORT +JniResult File__canWrite(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__canWrite, "canWrite", "()Z"); + if (_m_File__canWrite == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__canWrite); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__exists = NULL; +FFI_PLUGIN_EXPORT +JniResult File__exists(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__exists, "exists", "()Z"); + if (_m_File__exists == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__exists); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__isDirectory = NULL; +FFI_PLUGIN_EXPORT +JniResult File__isDirectory(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__isDirectory, "isDirectory", "()Z"); + if (_m_File__isDirectory == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__isDirectory); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__isFile = NULL; +FFI_PLUGIN_EXPORT +JniResult File__isFile(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__isFile, "isFile", "()Z"); + if (_m_File__isFile == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__isFile); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__isHidden = NULL; +FFI_PLUGIN_EXPORT +JniResult File__isHidden(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__isHidden, "isHidden", "()Z"); + if (_m_File__isHidden == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__isHidden); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__lastModified = NULL; +FFI_PLUGIN_EXPORT +JniResult File__lastModified(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__lastModified, "lastModified", "()J"); + if (_m_File__lastModified == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int64_t _result = + (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__lastModified); + return (JniResult){.value = {.j = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__length = NULL; +FFI_PLUGIN_EXPORT +JniResult File__length(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__length, "length", "()J"); + if (_m_File__length == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int64_t _result = (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__length); + return (JniResult){.value = {.j = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__createNewFile = NULL; +FFI_PLUGIN_EXPORT +JniResult File__createNewFile(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__createNewFile, "createNewFile", "()Z"); + if (_m_File__createNewFile == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__createNewFile); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__delete = NULL; +FFI_PLUGIN_EXPORT +JniResult File__delete(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__delete, "delete", "()Z"); + if (_m_File__delete == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__delete); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__deleteOnExit = NULL; +FFI_PLUGIN_EXPORT +JniResult File__deleteOnExit(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__deleteOnExit, "deleteOnExit", "()V"); + if (_m_File__deleteOnExit == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_File__deleteOnExit); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jmethodID _m_File__list = NULL; +FFI_PLUGIN_EXPORT +JniResult File__list(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__list, "list", "()[Ljava/lang/String;"); + if (_m_File__list == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__list); + return to_global_ref_result(_result); +} + +jmethodID _m_File__list1 = NULL; +FFI_PLUGIN_EXPORT +JniResult File__list1(jobject self_, jobject filenameFilter) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__list1, "list", + "(Ljava/io/FilenameFilter;)[Ljava/lang/String;"); + if (_m_File__list1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__list1, + filenameFilter); + return to_global_ref_result(_result); +} + +jmethodID _m_File__listFiles = NULL; +FFI_PLUGIN_EXPORT +JniResult File__listFiles(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__listFiles, "listFiles", "()[Ljava/io/File;"); + if (_m_File__listFiles == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__listFiles); + return to_global_ref_result(_result); +} + +jmethodID _m_File__listFiles1 = NULL; +FFI_PLUGIN_EXPORT +JniResult File__listFiles1(jobject self_, jobject filenameFilter) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__listFiles1, "listFiles", + "(Ljava/io/FilenameFilter;)[Ljava/io/File;"); + if (_m_File__listFiles1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_File__listFiles1, filenameFilter); + return to_global_ref_result(_result); +} + +jmethodID _m_File__listFiles2 = NULL; +FFI_PLUGIN_EXPORT +JniResult File__listFiles2(jobject self_, jobject fileFilter) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__listFiles2, "listFiles", + "(Ljava/io/FileFilter;)[Ljava/io/File;"); + if (_m_File__listFiles2 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_File__listFiles2, fileFilter); + return to_global_ref_result(_result); +} + +jmethodID _m_File__mkdir = NULL; +FFI_PLUGIN_EXPORT +JniResult File__mkdir(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__mkdir, "mkdir", "()Z"); + if (_m_File__mkdir == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__mkdir); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__mkdirs = NULL; +FFI_PLUGIN_EXPORT +JniResult File__mkdirs(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__mkdirs, "mkdirs", "()Z"); + if (_m_File__mkdirs == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__mkdirs); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__renameTo = NULL; +FFI_PLUGIN_EXPORT +JniResult File__renameTo(jobject self_, jobject file) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__renameTo, "renameTo", "(Ljava/io/File;)Z"); + if (_m_File__renameTo == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__renameTo, file); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__setLastModified = NULL; +FFI_PLUGIN_EXPORT +JniResult File__setLastModified(jobject self_, int64_t j) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__setLastModified, "setLastModified", "(J)Z"); + if (_m_File__setLastModified == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setLastModified, j); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__setReadOnly = NULL; +FFI_PLUGIN_EXPORT +JniResult File__setReadOnly(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__setReadOnly, "setReadOnly", "()Z"); + if (_m_File__setReadOnly == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setReadOnly); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__setWritable = NULL; +FFI_PLUGIN_EXPORT +JniResult File__setWritable(jobject self_, uint8_t z, uint8_t z1) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__setWritable, "setWritable", "(ZZ)Z"); + if (_m_File__setWritable == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setWritable, z, z1); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__setWritable1 = NULL; +FFI_PLUGIN_EXPORT +JniResult File__setWritable1(jobject self_, uint8_t z) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__setWritable1, "setWritable", "(Z)Z"); + if (_m_File__setWritable1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setWritable1, z); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__setReadable = NULL; +FFI_PLUGIN_EXPORT +JniResult File__setReadable(jobject self_, uint8_t z, uint8_t z1) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__setReadable, "setReadable", "(ZZ)Z"); + if (_m_File__setReadable == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setReadable, z, z1); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__setReadable1 = NULL; +FFI_PLUGIN_EXPORT +JniResult File__setReadable1(jobject self_, uint8_t z) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__setReadable1, "setReadable", "(Z)Z"); + if (_m_File__setReadable1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setReadable1, z); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__setExecutable = NULL; +FFI_PLUGIN_EXPORT +JniResult File__setExecutable(jobject self_, uint8_t z, uint8_t z1) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__setExecutable, "setExecutable", "(ZZ)Z"); + if (_m_File__setExecutable == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = (*jniEnv)->CallBooleanMethod(jniEnv, self_, + _m_File__setExecutable, z, z1); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__setExecutable1 = NULL; +FFI_PLUGIN_EXPORT +JniResult File__setExecutable1(jobject self_, uint8_t z) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__setExecutable1, "setExecutable", "(Z)Z"); + if (_m_File__setExecutable1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setExecutable1, z); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__canExecute = NULL; +FFI_PLUGIN_EXPORT +JniResult File__canExecute(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__canExecute, "canExecute", "()Z"); + if (_m_File__canExecute == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__canExecute); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__listRoots = NULL; +FFI_PLUGIN_EXPORT +JniResult File__listRoots() { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_method(_c_File, &_m_File__listRoots, "listRoots", + "()[Ljava/io/File;"); + if (_m_File__listRoots == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->CallStaticObjectMethod(jniEnv, _c_File, _m_File__listRoots); + return to_global_ref_result(_result); +} + +jmethodID _m_File__getTotalSpace = NULL; +FFI_PLUGIN_EXPORT +JniResult File__getTotalSpace(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__getTotalSpace, "getTotalSpace", "()J"); + if (_m_File__getTotalSpace == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int64_t _result = + (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__getTotalSpace); + return (JniResult){.value = {.j = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__getFreeSpace = NULL; +FFI_PLUGIN_EXPORT +JniResult File__getFreeSpace(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__getFreeSpace, "getFreeSpace", "()J"); + if (_m_File__getFreeSpace == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int64_t _result = + (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__getFreeSpace); + return (JniResult){.value = {.j = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__getUsableSpace = NULL; +FFI_PLUGIN_EXPORT +JniResult File__getUsableSpace(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__getUsableSpace, "getUsableSpace", "()J"); + if (_m_File__getUsableSpace == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int64_t _result = + (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__getUsableSpace); + return (JniResult){.value = {.j = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__createTempFile = NULL; +FFI_PLUGIN_EXPORT +JniResult File__createTempFile(jobject string, jobject string1, jobject file) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_method( + _c_File, &_m_File__createTempFile, "createTempFile", + "(Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)Ljava/io/File;"); + if (_m_File__createTempFile == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallStaticObjectMethod( + jniEnv, _c_File, _m_File__createTempFile, string, string1, file); + return to_global_ref_result(_result); +} + +jmethodID _m_File__createTempFile1 = NULL; +FFI_PLUGIN_EXPORT +JniResult File__createTempFile1(jobject string, jobject string1) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_method(_c_File, &_m_File__createTempFile1, "createTempFile", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/io/File;"); + if (_m_File__createTempFile1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallStaticObjectMethod( + jniEnv, _c_File, _m_File__createTempFile1, string, string1); + return to_global_ref_result(_result); +} + +jmethodID _m_File__compareTo = NULL; +FFI_PLUGIN_EXPORT +JniResult File__compareTo(jobject self_, jobject file) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__compareTo, "compareTo", "(Ljava/io/File;)I"); + if (_m_File__compareTo == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int32_t _result = + (*jniEnv)->CallIntMethod(jniEnv, self_, _m_File__compareTo, file); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__equals1 = NULL; +FFI_PLUGIN_EXPORT +JniResult File__equals1(jobject self_, jobject object) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__equals1, "equals", "(Ljava/lang/Object;)Z"); + if (_m_File__equals1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__equals1, object); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__hashCode1 = NULL; +FFI_PLUGIN_EXPORT +JniResult File__hashCode1(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__hashCode1, "hashCode", "()I"); + if (_m_File__hashCode1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int32_t _result = (*jniEnv)->CallIntMethod(jniEnv, self_, _m_File__hashCode1); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; +} + +jmethodID _m_File__toString1 = NULL; +FFI_PLUGIN_EXPORT +JniResult File__toString1(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__toString1, "toString", "()Ljava/lang/String;"); + if (_m_File__toString1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__toString1); + return to_global_ref_result(_result); +} + +jmethodID _m_File__toPath = NULL; +FFI_PLUGIN_EXPORT +JniResult File__toPath(jobject self_) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__toPath, "toPath", "()Ljava/nio/file/Path;"); + if (_m_File__toPath == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__toPath); + return to_global_ref_result(_result); +} + +jmethodID _m_File__compareTo1 = NULL; +FFI_PLUGIN_EXPORT +JniResult File__compareTo1(jobject self_, jobject object) { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__compareTo1, "compareTo", + "(Ljava/lang/Object;)I"); + if (_m_File__compareTo1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int32_t _result = + (*jniEnv)->CallIntMethod(jniEnv, self_, _m_File__compareTo1, object); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; +} + +jfieldID _f_File__pathSeparator = NULL; +FFI_PLUGIN_EXPORT +JniResult get_File__pathSeparator() { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_field(_c_File, &_f_File__pathSeparator, "pathSeparator", + "Ljava/lang/String;"); + jobject _result = + (*jniEnv)->GetStaticObjectField(jniEnv, _c_File, _f_File__pathSeparator); + return to_global_ref_result(_result); +} + +jfieldID _f_File__pathSeparatorChar = NULL; +FFI_PLUGIN_EXPORT +JniResult get_File__pathSeparatorChar() { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_field(_c_File, &_f_File__pathSeparatorChar, "pathSeparatorChar", + "C"); + uint16_t _result = (*jniEnv)->GetStaticCharField(jniEnv, _c_File, + _f_File__pathSeparatorChar); + return (JniResult){.value = {.c = _result}, .exception = check_exception()}; +} + +jfieldID _f_File__separator = NULL; +FFI_PLUGIN_EXPORT +JniResult get_File__separator() { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_field(_c_File, &_f_File__separator, "separator", + "Ljava/lang/String;"); + jobject _result = + (*jniEnv)->GetStaticObjectField(jniEnv, _c_File, _f_File__separator); + return to_global_ref_result(_result); +} + +jfieldID _f_File__separatorChar = NULL; +FFI_PLUGIN_EXPORT +JniResult get_File__separatorChar() { + load_env(); + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_field(_c_File, &_f_File__separatorChar, "separatorChar", "C"); + uint16_t _result = + (*jniEnv)->GetStaticCharField(jniEnv, _c_File, _f_File__separatorChar); + return (JniResult){.value = {.c = _result}, .exception = check_exception()}; +} diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index 7224535df4..a31ce77d3e 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -152,7 +152,7 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { ) // FIXME temporary - SentryFlutterReplay.cacheDir = File(options.cacheDirPath!!, "replay_0") + SentryFlutterReplay.cacheDir = File(options.cacheDirPath!!, "replay_0").path options.addIntegration(replay) options.setReplayController(replay) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt index 96fff0c018..eb913412b0 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt @@ -1,6 +1,7 @@ package io.sentry.flutter import io.sentry.android.replay.Recorder +import io.sentry.android.replay.ScreenshotRecorderCallback object SentryFlutterReplay { // Set by the Flutter side, read during SentryAndroid.init() diff --git a/flutter/ffi-jni.yaml b/flutter/ffi-jni.yaml index 55c488eea1..14c17958b0 100644 --- a/flutter/ffi-jni.yaml +++ b/flutter/ffi-jni.yaml @@ -18,7 +18,9 @@ log_level: all classes: - io.sentry.android.replay.Recorder - io.sentry.android.replay.ScreenshotRecorderConfig + - io.sentry.android.replay.ScreenshotRecorderCallback - io.sentry.flutter.SentryFlutterReplay + - java.io.File enable_experiment: - interface_implementation diff --git a/flutter/lib/src/native/java/android_replay_recorder.dart b/flutter/lib/src/native/java/android_replay_recorder.dart index 57d0260fe3..951e7bab7b 100644 --- a/flutter/lib/src/native/java/android_replay_recorder.dart +++ b/flutter/lib/src/native/java/android_replay_recorder.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'dart:ui'; +import 'package:jni/jni.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; @@ -11,6 +12,7 @@ import 'binding.dart' as java; @internal class AndroidReplayRecorder implements java.$RecorderImpl { late ScreenshotRecorder _recorder; + late java.ScreenshotRecorderCallback? _callback; AndroidReplayRecorder._(); @@ -25,7 +27,9 @@ class AndroidReplayRecorder implements java.$RecorderImpl { @override void start(java.ScreenshotRecorderConfig config) { - var cacheDir = java.SentryFlutterReplay.cacheDir; + var cacheDir = + java.SentryFlutterReplay.cacheDir.toDartString(releaseOriginal: true); + _callback = java.SentryFlutterReplay.callback; _recorder = ScreenshotRecorder( ScreenshotRecorderConfig( config.getRecordingWidth(), @@ -35,14 +39,28 @@ class AndroidReplayRecorder implements java.$RecorderImpl { ), (image) async { var imageData = await image.toByteData(format: ImageByteFormat.png); if (imageData != null) { - var filePath = - path.join(cacheDir, "${DateTime.now().millisecondsSinceEpoch}.png"); + var timestamp = DateTime.now().millisecondsSinceEpoch; + var filePath = path.join(cacheDir, "$timestamp.png"); await File(filePath).writeAsBytes(imageData.buffer.asUint8List()); + + var jFilePath = filePath.toJString(); + var jFile = java.File(jFilePath); + try { + _callback?.onScreenshotRecorded1(jFile, timestamp); + } finally { + jFile.release(); + jFilePath.release(); + } } }); _recorder.start(); } @override - void stop() => _recorder.stop(); + void stop() { + _recorder.stop(); + var callback = _callback; + _callback = null; + callback?.release(); + } } diff --git a/flutter/lib/src/native/java/binding.dart b/flutter/lib/src/native/java/binding.dart index c077d145cc..d4718ebe11 100644 --- a/flutter/lib/src/native/java/binding.dart +++ b/flutter/lib/src/native/java/binding.dart @@ -583,6 +583,188 @@ class $ScreenshotRecorderConfigType } } +/// from: io.sentry.android.replay.ScreenshotRecorderCallback +class ScreenshotRecorderCallback extends jni.JObject { + @override + late final jni.JObjType $type = type; + + ScreenshotRecorderCallback.fromRef( + jni.JObjectPtr ref, + ) : super.fromRef(ref); + + /// The type which includes information such as the signature of this class. + static const type = $ScreenshotRecorderCallbackType(); + static final _onScreenshotRecorded = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>>( + "ScreenshotRecorderCallback__onScreenshotRecorded") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public abstract void onScreenshotRecorded(android.graphics.Bitmap bitmap) + void onScreenshotRecorded( + jni.JObject bitmap, + ) { + return _onScreenshotRecorded(reference, bitmap.reference).check(); + } + + static final _onScreenshotRecorded1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer, ffi.Int64)>>( + "ScreenshotRecorderCallback__onScreenshotRecorded1") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer, int)>(); + + /// from: public abstract void onScreenshotRecorded(java.io.File file, long j) + void onScreenshotRecorded1( + File file, + int j, + ) { + return _onScreenshotRecorded1(reference, file.reference, j).check(); + } + + /// Maps a specific port to the implemented interface. + static final Map _$impls = {}; + ReceivePort? _$p; + + static jni.JObjectPtr _$invoke( + int port, + jni.JObjectPtr descriptor, + jni.JObjectPtr args, + ) { + return _$invokeMethod( + port, + $MethodInvocation.fromAddresses( + 0, + descriptor.address, + args.address, + ), + ); + } + + static final ffi.Pointer< + ffi.NativeFunction< + jni.JObjectPtr Function( + ffi.Uint64, jni.JObjectPtr, jni.JObjectPtr)>> + _$invokePointer = ffi.Pointer.fromFunction(_$invoke); + + static ffi.Pointer _$invokeMethod( + int $p, + $MethodInvocation $i, + ) { + try { + final $d = $i.methodDescriptor.toDartString(releaseOriginal: true); + final $a = $i.args; + if ($d == r"onScreenshotRecorded(Landroid/graphics/Bitmap;)V") { + _$impls[$p]!.onScreenshotRecorded( + $a[0].castTo(const jni.JObjectType(), releaseOriginal: true), + ); + return jni.nullptr; + } + if ($d == r"onScreenshotRecorded(Ljava/io/File;J)V") { + _$impls[$p]!.onScreenshotRecorded1( + $a[0].castTo(const $FileType(), releaseOriginal: true), + $a[1] + .castTo(const jni.JLongType(), releaseOriginal: true) + .longValue(releaseOriginal: true), + ); + return jni.nullptr; + } + } catch (e) { + return ProtectedJniExtensions.newDartException(e.toString()); + } + return jni.nullptr; + } + + factory ScreenshotRecorderCallback.implement( + $ScreenshotRecorderCallbackImpl $impl, + ) { + final $p = ReceivePort(); + final $x = ScreenshotRecorderCallback.fromRef( + ProtectedJniExtensions.newPortProxy( + r"io.sentry.android.replay.ScreenshotRecorderCallback", + $p, + _$invokePointer, + ), + ).._$p = $p; + final $a = $p.sendPort.nativePort; + _$impls[$a] = $impl; + $p.listen(($m) { + if ($m == null) { + _$impls.remove($p.sendPort.nativePort); + $p.close(); + return; + } + final $i = $MethodInvocation.fromMessage($m); + final $r = _$invokeMethod($p.sendPort.nativePort, $i); + ProtectedJniExtensions.returnResult($i.result, $r); + }); + return $x; + } +} + +abstract class $ScreenshotRecorderCallbackImpl { + factory $ScreenshotRecorderCallbackImpl({ + required void Function(jni.JObject bitmap) onScreenshotRecorded, + required void Function(File file, int j) onScreenshotRecorded1, + }) = _$ScreenshotRecorderCallbackImpl; + + void onScreenshotRecorded(jni.JObject bitmap); + void onScreenshotRecorded1(File file, int j); +} + +class _$ScreenshotRecorderCallbackImpl + implements $ScreenshotRecorderCallbackImpl { + _$ScreenshotRecorderCallbackImpl({ + required void Function(jni.JObject bitmap) onScreenshotRecorded, + required void Function(File file, int j) onScreenshotRecorded1, + }) : _onScreenshotRecorded = onScreenshotRecorded, + _onScreenshotRecorded1 = onScreenshotRecorded1; + + final void Function(jni.JObject bitmap) _onScreenshotRecorded; + final void Function(File file, int j) _onScreenshotRecorded1; + + void onScreenshotRecorded(jni.JObject bitmap) { + return _onScreenshotRecorded(bitmap); + } + + void onScreenshotRecorded1(File file, int j) { + return _onScreenshotRecorded1(file, j); + } +} + +class $ScreenshotRecorderCallbackType + extends jni.JObjType { + const $ScreenshotRecorderCallbackType(); + + @override + String get signature => + r"Lio/sentry/android/replay/ScreenshotRecorderCallback;"; + + @override + ScreenshotRecorderCallback fromRef(jni.JObjectPtr ref) => + ScreenshotRecorderCallback.fromRef(ref); + + @override + jni.JObjType get superType => const jni.JObjectType(); + + @override + final superCount = 1; + + @override + int get hashCode => ($ScreenshotRecorderCallbackType).hashCode; + + @override + bool operator ==(Object other) { + return other.runtimeType == ($ScreenshotRecorderCallbackType) && + other is $ScreenshotRecorderCallbackType; + } +} + /// from: io.sentry.flutter.SentryFlutterReplay class SentryFlutterReplay extends jni.JObject { @override @@ -624,6 +806,48 @@ class SentryFlutterReplay extends jni.JObject { /// The returned object must be released after use, by calling the [release] method. static set recorder(Recorder value) => _set_recorder(value.reference).check(); + static final _get_cacheDir = + jniLookup>( + "get_SentryFlutterReplay__cacheDir") + .asFunction(); + + static final _set_cacheDir = jniLookup< + ffi + .NativeFunction)>>( + "set_SentryFlutterReplay__cacheDir") + .asFunction)>(); + + /// from: static public java.lang.String cacheDir + /// The returned object must be released after use, by calling the [release] method. + static jni.JString get cacheDir => + const jni.JStringType().fromRef(_get_cacheDir().object); + + /// from: static public java.lang.String cacheDir + /// The returned object must be released after use, by calling the [release] method. + static set cacheDir(jni.JString value) => + _set_cacheDir(value.reference).check(); + + static final _get_callback = + jniLookup>( + "get_SentryFlutterReplay__callback") + .asFunction(); + + static final _set_callback = jniLookup< + ffi + .NativeFunction)>>( + "set_SentryFlutterReplay__callback") + .asFunction)>(); + + /// from: static public io.sentry.android.replay.ScreenshotRecorderCallback callback + /// The returned object must be released after use, by calling the [release] method. + static ScreenshotRecorderCallback get callback => + const $ScreenshotRecorderCallbackType().fromRef(_get_callback().object); + + /// from: static public io.sentry.android.replay.ScreenshotRecorderCallback callback + /// The returned object must be released after use, by calling the [release] method. + static set callback(ScreenshotRecorderCallback value) => + _set_callback(value.reference).check(); + static final _getRecorder = jniLookup< ffi .NativeFunction)>>( @@ -650,6 +874,62 @@ class SentryFlutterReplay extends jni.JObject { ) { return _setRecorder(reference, recorder.reference).check(); } + + static final _getCacheDir = jniLookup< + ffi + .NativeFunction)>>( + "SentryFlutterReplay__getCacheDir") + .asFunction)>(); + + /// from: public final java.lang.String getCacheDir() + /// The returned object must be released after use, by calling the [release] method. + jni.JString getCacheDir() { + return const jni.JStringType().fromRef(_getCacheDir(reference).object); + } + + static final _setCacheDir = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("SentryFlutterReplay__setCacheDir") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public final void setCacheDir(java.lang.String string) + void setCacheDir( + jni.JString string, + ) { + return _setCacheDir(reference, string.reference).check(); + } + + static final _getCallback = jniLookup< + ffi + .NativeFunction)>>( + "SentryFlutterReplay__getCallback") + .asFunction)>(); + + /// from: public final io.sentry.android.replay.ScreenshotRecorderCallback getCallback() + /// The returned object must be released after use, by calling the [release] method. + ScreenshotRecorderCallback getCallback() { + return const $ScreenshotRecorderCallbackType() + .fromRef(_getCallback(reference).object); + } + + static final _setCallback = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("SentryFlutterReplay__setCallback") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public final void setCallback(io.sentry.android.replay.ScreenshotRecorderCallback screenshotRecorderCallback) + void setCallback( + ScreenshotRecorderCallback screenshotRecorderCallback, + ) { + return _setCallback(reference, screenshotRecorderCallback.reference) + .check(); + } } class $SentryFlutterReplayType extends jni.JObjType { @@ -677,3 +957,761 @@ class $SentryFlutterReplayType extends jni.JObjType { other is $SentryFlutterReplayType; } } + +/// from: java.io.File +class File extends jni.JObject { + @override + late final jni.JObjType $type = type; + + File.fromRef( + jni.JObjectPtr ref, + ) : super.fromRef(ref); + + /// The type which includes information such as the signature of this class. + static const type = $FileType(); + static final _get_pathSeparator = + jniLookup>( + "get_File__pathSeparator") + .asFunction(); + + /// from: static public final java.lang.String pathSeparator + /// The returned object must be released after use, by calling the [release] method. + static jni.JString get pathSeparator => + const jni.JStringType().fromRef(_get_pathSeparator().object); + + static final _get_pathSeparatorChar = + jniLookup>( + "get_File__pathSeparatorChar") + .asFunction(); + + /// from: static public final char pathSeparatorChar + static int get pathSeparatorChar => _get_pathSeparatorChar().char; + + static final _get_separator = + jniLookup>( + "get_File__separator") + .asFunction(); + + /// from: static public final java.lang.String separator + /// The returned object must be released after use, by calling the [release] method. + static jni.JString get separator => + const jni.JStringType().fromRef(_get_separator().object); + + static final _get_separatorChar = + jniLookup>( + "get_File__separatorChar") + .asFunction(); + + /// from: static public final char separatorChar + static int get separatorChar => _get_separatorChar().char; + + static final _new0 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__new0") + .asFunction)>(); + + /// from: public void (java.lang.String string) + /// The returned object must be released after use, by calling the [release] method. + factory File( + jni.JString string, + ) { + return File.fromRef(_new0(string.reference).object); + } + + static final _new1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>>("File__new1") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public void (java.lang.String string, java.lang.String string1) + /// The returned object must be released after use, by calling the [release] method. + factory File.new1( + jni.JString string, + jni.JString string1, + ) { + return File.fromRef(_new1(string.reference, string1.reference).object); + } + + static final _new2 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>>("File__new2") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public void (java.io.File file, java.lang.String string) + /// The returned object must be released after use, by calling the [release] method. + factory File.new2( + File file, + jni.JString string, + ) { + return File.fromRef(_new2(file.reference, string.reference).object); + } + + static final _new3 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__new3") + .asFunction)>(); + + /// from: public void (java.net.URI uRI) + /// The returned object must be released after use, by calling the [release] method. + factory File.new3( + jni.JObject uRI, + ) { + return File.fromRef(_new3(uRI.reference).object); + } + + static final _getName = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__getName") + .asFunction)>(); + + /// from: public java.lang.String getName() + /// The returned object must be released after use, by calling the [release] method. + jni.JString getName() { + return const jni.JStringType().fromRef(_getName(reference).object); + } + + static final _getParent = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__getParent") + .asFunction)>(); + + /// from: public java.lang.String getParent() + /// The returned object must be released after use, by calling the [release] method. + jni.JString getParent() { + return const jni.JStringType().fromRef(_getParent(reference).object); + } + + static final _getParentFile = jniLookup< + ffi + .NativeFunction)>>( + "File__getParentFile") + .asFunction)>(); + + /// from: public java.io.File getParentFile() + /// The returned object must be released after use, by calling the [release] method. + File getParentFile() { + return const $FileType().fromRef(_getParentFile(reference).object); + } + + static final _getPath = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__getPath") + .asFunction)>(); + + /// from: public java.lang.String getPath() + /// The returned object must be released after use, by calling the [release] method. + jni.JString getPath() { + return const jni.JStringType().fromRef(_getPath(reference).object); + } + + static final _isAbsolute = jniLookup< + ffi + .NativeFunction)>>( + "File__isAbsolute") + .asFunction)>(); + + /// from: public boolean isAbsolute() + bool isAbsolute() { + return _isAbsolute(reference).boolean; + } + + static final _getAbsolutePath = jniLookup< + ffi + .NativeFunction)>>( + "File__getAbsolutePath") + .asFunction)>(); + + /// from: public java.lang.String getAbsolutePath() + /// The returned object must be released after use, by calling the [release] method. + jni.JString getAbsolutePath() { + return const jni.JStringType().fromRef(_getAbsolutePath(reference).object); + } + + static final _getAbsoluteFile = jniLookup< + ffi + .NativeFunction)>>( + "File__getAbsoluteFile") + .asFunction)>(); + + /// from: public java.io.File getAbsoluteFile() + /// The returned object must be released after use, by calling the [release] method. + File getAbsoluteFile() { + return const $FileType().fromRef(_getAbsoluteFile(reference).object); + } + + static final _getCanonicalPath = jniLookup< + ffi + .NativeFunction)>>( + "File__getCanonicalPath") + .asFunction)>(); + + /// from: public java.lang.String getCanonicalPath() + /// The returned object must be released after use, by calling the [release] method. + jni.JString getCanonicalPath() { + return const jni.JStringType().fromRef(_getCanonicalPath(reference).object); + } + + static final _getCanonicalFile = jniLookup< + ffi + .NativeFunction)>>( + "File__getCanonicalFile") + .asFunction)>(); + + /// from: public java.io.File getCanonicalFile() + /// The returned object must be released after use, by calling the [release] method. + File getCanonicalFile() { + return const $FileType().fromRef(_getCanonicalFile(reference).object); + } + + static final _toURL = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__toURL") + .asFunction)>(); + + /// from: public java.net.URL toURL() + /// The returned object must be released after use, by calling the [release] method. + jni.JObject toURL() { + return const jni.JObjectType().fromRef(_toURL(reference).object); + } + + static final _toURI = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__toURI") + .asFunction)>(); + + /// from: public java.net.URI toURI() + /// The returned object must be released after use, by calling the [release] method. + jni.JObject toURI() { + return const jni.JObjectType().fromRef(_toURI(reference).object); + } + + static final _canRead = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__canRead") + .asFunction)>(); + + /// from: public boolean canRead() + bool canRead() { + return _canRead(reference).boolean; + } + + static final _canWrite = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__canWrite") + .asFunction)>(); + + /// from: public boolean canWrite() + bool canWrite() { + return _canWrite(reference).boolean; + } + + static final _exists = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__exists") + .asFunction)>(); + + /// from: public boolean exists() + bool exists() { + return _exists(reference).boolean; + } + + static final _isDirectory = jniLookup< + ffi + .NativeFunction)>>( + "File__isDirectory") + .asFunction)>(); + + /// from: public boolean isDirectory() + bool isDirectory() { + return _isDirectory(reference).boolean; + } + + static final _isFile = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__isFile") + .asFunction)>(); + + /// from: public boolean isFile() + bool isFile() { + return _isFile(reference).boolean; + } + + static final _isHidden = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__isHidden") + .asFunction)>(); + + /// from: public boolean isHidden() + bool isHidden() { + return _isHidden(reference).boolean; + } + + static final _lastModified = jniLookup< + ffi + .NativeFunction)>>( + "File__lastModified") + .asFunction)>(); + + /// from: public long lastModified() + int lastModified() { + return _lastModified(reference).long; + } + + static final _length = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__length") + .asFunction)>(); + + /// from: public long length() + int length() { + return _length(reference).long; + } + + static final _createNewFile = jniLookup< + ffi + .NativeFunction)>>( + "File__createNewFile") + .asFunction)>(); + + /// from: public boolean createNewFile() + bool createNewFile() { + return _createNewFile(reference).boolean; + } + + static final _delete = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__delete") + .asFunction)>(); + + /// from: public boolean delete() + bool delete() { + return _delete(reference).boolean; + } + + static final _deleteOnExit = jniLookup< + ffi + .NativeFunction)>>( + "File__deleteOnExit") + .asFunction)>(); + + /// from: public void deleteOnExit() + void deleteOnExit() { + return _deleteOnExit(reference).check(); + } + + static final _list = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__list") + .asFunction)>(); + + /// from: public java.lang.String[] list() + /// The returned object must be released after use, by calling the [release] method. + jni.JArray list() { + return const jni.JArrayType(jni.JStringType()) + .fromRef(_list(reference).object); + } + + static final _list1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>>("File__list1") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public java.lang.String[] list(java.io.FilenameFilter filenameFilter) + /// The returned object must be released after use, by calling the [release] method. + jni.JArray list1( + jni.JObject filenameFilter, + ) { + return const jni.JArrayType(jni.JStringType()) + .fromRef(_list1(reference, filenameFilter.reference).object); + } + + static final _listFiles = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__listFiles") + .asFunction)>(); + + /// from: public java.io.File[] listFiles() + /// The returned object must be released after use, by calling the [release] method. + jni.JArray listFiles() { + return const jni.JArrayType($FileType()) + .fromRef(_listFiles(reference).object); + } + + static final _listFiles1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("File__listFiles1") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public java.io.File[] listFiles(java.io.FilenameFilter filenameFilter) + /// The returned object must be released after use, by calling the [release] method. + jni.JArray listFiles1( + jni.JObject filenameFilter, + ) { + return const jni.JArrayType($FileType()) + .fromRef(_listFiles1(reference, filenameFilter.reference).object); + } + + static final _listFiles2 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("File__listFiles2") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public java.io.File[] listFiles(java.io.FileFilter fileFilter) + /// The returned object must be released after use, by calling the [release] method. + jni.JArray listFiles2( + jni.JObject fileFilter, + ) { + return const jni.JArrayType($FileType()) + .fromRef(_listFiles2(reference, fileFilter.reference).object); + } + + static final _mkdir = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__mkdir") + .asFunction)>(); + + /// from: public boolean mkdir() + bool mkdir() { + return _mkdir(reference).boolean; + } + + static final _mkdirs = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__mkdirs") + .asFunction)>(); + + /// from: public boolean mkdirs() + bool mkdirs() { + return _mkdirs(reference).boolean; + } + + static final _renameTo = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("File__renameTo") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public boolean renameTo(java.io.File file) + bool renameTo( + File file, + ) { + return _renameTo(reference, file.reference).boolean; + } + + static final _setLastModified = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Int64)>>("File__setLastModified") + .asFunction, int)>(); + + /// from: public boolean setLastModified(long j) + bool setLastModified( + int j, + ) { + return _setLastModified(reference, j).boolean; + } + + static final _setReadOnly = jniLookup< + ffi + .NativeFunction)>>( + "File__setReadOnly") + .asFunction)>(); + + /// from: public boolean setReadOnly() + bool setReadOnly() { + return _setReadOnly(reference).boolean; + } + + static final _setWritable = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, ffi.Uint8, + ffi.Uint8)>>("File__setWritable") + .asFunction, int, int)>(); + + /// from: public boolean setWritable(boolean z, boolean z1) + bool setWritable( + bool z, + bool z1, + ) { + return _setWritable(reference, z ? 1 : 0, z1 ? 1 : 0).boolean; + } + + static final _setWritable1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Uint8)>>("File__setWritable1") + .asFunction, int)>(); + + /// from: public boolean setWritable(boolean z) + bool setWritable1( + bool z, + ) { + return _setWritable1(reference, z ? 1 : 0).boolean; + } + + static final _setReadable = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, ffi.Uint8, + ffi.Uint8)>>("File__setReadable") + .asFunction, int, int)>(); + + /// from: public boolean setReadable(boolean z, boolean z1) + bool setReadable( + bool z, + bool z1, + ) { + return _setReadable(reference, z ? 1 : 0, z1 ? 1 : 0).boolean; + } + + static final _setReadable1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Uint8)>>("File__setReadable1") + .asFunction, int)>(); + + /// from: public boolean setReadable(boolean z) + bool setReadable1( + bool z, + ) { + return _setReadable1(reference, z ? 1 : 0).boolean; + } + + static final _setExecutable = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, ffi.Uint8, + ffi.Uint8)>>("File__setExecutable") + .asFunction, int, int)>(); + + /// from: public boolean setExecutable(boolean z, boolean z1) + bool setExecutable( + bool z, + bool z1, + ) { + return _setExecutable(reference, z ? 1 : 0, z1 ? 1 : 0).boolean; + } + + static final _setExecutable1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Uint8)>>("File__setExecutable1") + .asFunction, int)>(); + + /// from: public boolean setExecutable(boolean z) + bool setExecutable1( + bool z, + ) { + return _setExecutable1(reference, z ? 1 : 0).boolean; + } + + static final _canExecute = jniLookup< + ffi + .NativeFunction)>>( + "File__canExecute") + .asFunction)>(); + + /// from: public boolean canExecute() + bool canExecute() { + return _canExecute(reference).boolean; + } + + static final _listRoots = + jniLookup>("File__listRoots") + .asFunction(); + + /// from: static public java.io.File[] listRoots() + /// The returned object must be released after use, by calling the [release] method. + static jni.JArray listRoots() { + return const jni.JArrayType($FileType()).fromRef(_listRoots().object); + } + + static final _getTotalSpace = jniLookup< + ffi + .NativeFunction)>>( + "File__getTotalSpace") + .asFunction)>(); + + /// from: public long getTotalSpace() + int getTotalSpace() { + return _getTotalSpace(reference).long; + } + + static final _getFreeSpace = jniLookup< + ffi + .NativeFunction)>>( + "File__getFreeSpace") + .asFunction)>(); + + /// from: public long getFreeSpace() + int getFreeSpace() { + return _getFreeSpace(reference).long; + } + + static final _getUsableSpace = jniLookup< + ffi + .NativeFunction)>>( + "File__getUsableSpace") + .asFunction)>(); + + /// from: public long getUsableSpace() + int getUsableSpace() { + return _getUsableSpace(reference).long; + } + + static final _createTempFile = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>>("File__createTempFile") + .asFunction< + jni.JniResult Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + /// from: static public java.io.File createTempFile(java.lang.String string, java.lang.String string1, java.io.File file) + /// The returned object must be released after use, by calling the [release] method. + static File createTempFile( + jni.JString string, + jni.JString string1, + File file, + ) { + return const $FileType().fromRef( + _createTempFile(string.reference, string1.reference, file.reference) + .object); + } + + static final _createTempFile1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("File__createTempFile1") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: static public java.io.File createTempFile(java.lang.String string, java.lang.String string1) + /// The returned object must be released after use, by calling the [release] method. + static File createTempFile1( + jni.JString string, + jni.JString string1, + ) { + return const $FileType() + .fromRef(_createTempFile1(string.reference, string1.reference).object); + } + + static final _compareTo = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("File__compareTo") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public int compareTo(java.io.File file) + int compareTo( + File file, + ) { + return _compareTo(reference, file.reference).integer; + } + + static final _equals1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("File__equals1") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public boolean equals(java.lang.Object object) + bool equals1( + jni.JObject object, + ) { + return _equals1(reference, object.reference).boolean; + } + + static final _hashCode1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__hashCode1") + .asFunction)>(); + + /// from: public int hashCode() + int hashCode1() { + return _hashCode1(reference).integer; + } + + static final _toString1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__toString1") + .asFunction)>(); + + /// from: public java.lang.String toString() + /// The returned object must be released after use, by calling the [release] method. + jni.JString toString1() { + return const jni.JStringType().fromRef(_toString1(reference).object); + } + + static final _toPath = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__toPath") + .asFunction)>(); + + /// from: public java.nio.file.Path toPath() + /// The returned object must be released after use, by calling the [release] method. + jni.JObject toPath() { + return const jni.JObjectType().fromRef(_toPath(reference).object); + } + + static final _compareTo1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("File__compareTo1") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public int compareTo(java.lang.Object object) + int compareTo1( + jni.JObject object, + ) { + return _compareTo1(reference, object.reference).integer; + } +} + +class $FileType extends jni.JObjType { + const $FileType(); + + @override + String get signature => r"Ljava/io/File;"; + + @override + File fromRef(jni.JObjectPtr ref) => File.fromRef(ref); + + @override + jni.JObjType get superType => const jni.JObjectType(); + + @override + final superCount = 1; + + @override + int get hashCode => ($FileType).hashCode; + + @override + bool operator ==(Object other) { + return other.runtimeType == ($FileType) && other is $FileType; + } +} From 87d18e777072d453a48d0afb55247f58bb6fcfc5 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 22 Apr 2024 17:50:46 +0200 Subject: [PATCH 15/74] glue code for jni --- .../src/main/cpp/sentry_android_binding.c | 424 ++++++++++++---- .../io/sentry/flutter/SentryFlutterPlugin.kt | 13 +- .../io/sentry/flutter/SentryFlutterReplay.kt | 6 +- flutter/example/lib/main.dart | 2 + flutter/ffi-jni.yaml | 2 +- .../native/java/android_replay_recorder.dart | 18 +- flutter/lib/src/native/java/binding.dart | 464 +++++++++++------- 7 files changed, 611 insertions(+), 318 deletions(-) diff --git a/flutter/android/src/main/cpp/sentry_android_binding.c b/flutter/android/src/main/cpp/sentry_android_binding.c index ec9ac12159..9151710183 100644 --- a/flutter/android/src/main/cpp/sentry_android_binding.c +++ b/flutter/android/src/main/cpp/sentry_android_binding.c @@ -454,47 +454,322 @@ JniResult get_ScreenshotRecorderConfig__Companion() { return to_global_ref_result(_result); } -// io.sentry.android.replay.ScreenshotRecorderCallback -jclass _c_ScreenshotRecorderCallback = NULL; +// io.sentry.android.replay.ReplayIntegration +jclass _c_ReplayIntegration = NULL; -jmethodID _m_ScreenshotRecorderCallback__onScreenshotRecorded = NULL; +jmethodID _m_ReplayIntegration__new0 = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderCallback__onScreenshotRecorded(jobject self_, - jobject bitmap) { +JniResult ReplayIntegration__new0(jobject context, + jobject iCurrentDateProvider, + jobject function0, + jobject function1, + jobject function11) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderCallback, - "io/sentry/android/replay/ScreenshotRecorderCallback"); - if (_c_ScreenshotRecorderCallback == NULL) + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderCallback, - &_m_ScreenshotRecorderCallback__onScreenshotRecorded, + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__new0, "", + "(Landroid/content/Context;Lio/sentry/transport/" + "ICurrentDateProvider;Lkotlin/jvm/functions/Function0;Lkotlin/" + "jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V"); + if (_m_ReplayIntegration__new0 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->NewObject( + jniEnv, _c_ReplayIntegration, _m_ReplayIntegration__new0, context, + iCurrentDateProvider, function0, function1, function11); + return to_global_ref_result(_result); +} + +jmethodID _m_ReplayIntegration__new1 = NULL; +FFI_PLUGIN_EXPORT +JniResult ReplayIntegration__new1(jobject context, + jobject iCurrentDateProvider, + jobject function0, + jobject function1, + jobject function11, + int32_t i, + jobject defaultConstructorMarker) { + load_env(); + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__new1, "", + "(Landroid/content/Context;Lio/sentry/transport/" + "ICurrentDateProvider;Lkotlin/jvm/functions/Function0;Lkotlin/" + "jvm/functions/Function1;Lkotlin/jvm/functions/" + "Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V"); + if (_m_ReplayIntegration__new1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->NewObject( + jniEnv, _c_ReplayIntegration, _m_ReplayIntegration__new1, context, + iCurrentDateProvider, function0, function1, function11, i, + defaultConstructorMarker); + return to_global_ref_result(_result); +} + +jmethodID _m_ReplayIntegration__new2 = NULL; +FFI_PLUGIN_EXPORT +JniResult ReplayIntegration__new2(jobject context, + jobject iCurrentDateProvider) { + load_env(); + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method( + _c_ReplayIntegration, &_m_ReplayIntegration__new2, "", + "(Landroid/content/Context;Lio/sentry/transport/ICurrentDateProvider;)V"); + if (_m_ReplayIntegration__new2 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->NewObject(jniEnv, _c_ReplayIntegration, + _m_ReplayIntegration__new2, context, + iCurrentDateProvider); + return to_global_ref_result(_result); +} + +jmethodID _m_ReplayIntegration__getReplayCacheDir = NULL; +FFI_PLUGIN_EXPORT +JniResult ReplayIntegration__getReplayCacheDir(jobject self_) { + load_env(); + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__getReplayCacheDir, + "getReplayCacheDir", "()Ljava/io/File;"); + if (_m_ReplayIntegration__getReplayCacheDir == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_ReplayIntegration__getReplayCacheDir); + return to_global_ref_result(_result); +} + +jmethodID _m_ReplayIntegration__register = NULL; +FFI_PLUGIN_EXPORT +JniResult ReplayIntegration__register(jobject self_, + jobject iHub, + jobject sentryOptions) { + load_env(); + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__register, "register", + "(Lio/sentry/IHub;Lio/sentry/SentryOptions;)V"); + if (_m_ReplayIntegration__register == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__register, iHub, + sentryOptions); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jmethodID _m_ReplayIntegration__isRecording = NULL; +FFI_PLUGIN_EXPORT +JniResult ReplayIntegration__isRecording(jobject self_) { + load_env(); + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__isRecording, + "isRecording", "()Z"); + if (_m_ReplayIntegration__isRecording == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = (*jniEnv)->CallBooleanMethod( + jniEnv, self_, _m_ReplayIntegration__isRecording); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; +} + +jmethodID _m_ReplayIntegration__start = NULL; +FFI_PLUGIN_EXPORT +JniResult ReplayIntegration__start(jobject self_) { + load_env(); + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__start, "start", + "()V"); + if (_m_ReplayIntegration__start == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__start); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jmethodID _m_ReplayIntegration__resume = NULL; +FFI_PLUGIN_EXPORT +JniResult ReplayIntegration__resume(jobject self_) { + load_env(); + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__resume, "resume", + "()V"); + if (_m_ReplayIntegration__resume == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__resume); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jmethodID _m_ReplayIntegration__sendReplayForEvent = NULL; +FFI_PLUGIN_EXPORT +JniResult ReplayIntegration__sendReplayForEvent(jobject self_, + jobject sentryEvent, + jobject hint) { + load_env(); + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__sendReplayForEvent, + "sendReplayForEvent", + "(Lio/sentry/SentryEvent;Lio/sentry/Hint;)V"); + if (_m_ReplayIntegration__sendReplayForEvent == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, + _m_ReplayIntegration__sendReplayForEvent, + sentryEvent, hint); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jmethodID _m_ReplayIntegration__getReplayId = NULL; +FFI_PLUGIN_EXPORT +JniResult ReplayIntegration__getReplayId(jobject self_) { + load_env(); + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__getReplayId, + "getReplayId", "()Lio/sentry/protocol/SentryId;"); + if (_m_ReplayIntegration__getReplayId == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_ReplayIntegration__getReplayId); + return to_global_ref_result(_result); +} + +jmethodID _m_ReplayIntegration__pause = NULL; +FFI_PLUGIN_EXPORT +JniResult ReplayIntegration__pause(jobject self_) { + load_env(); + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__pause, "pause", + "()V"); + if (_m_ReplayIntegration__pause == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__pause); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jmethodID _m_ReplayIntegration__stop = NULL; +FFI_PLUGIN_EXPORT +JniResult ReplayIntegration__stop(jobject self_) { + load_env(); + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__stop, "stop", "()V"); + if (_m_ReplayIntegration__stop == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__stop); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jmethodID _m_ReplayIntegration__onScreenshotRecorded = NULL; +FFI_PLUGIN_EXPORT +JniResult ReplayIntegration__onScreenshotRecorded(jobject self_, + jobject bitmap) { + load_env(); + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__onScreenshotRecorded, "onScreenshotRecorded", "(Landroid/graphics/Bitmap;)V"); - if (_m_ScreenshotRecorderCallback__onScreenshotRecorded == NULL) + if (_m_ReplayIntegration__onScreenshotRecorded == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; (*jniEnv)->CallVoidMethod(jniEnv, self_, - _m_ScreenshotRecorderCallback__onScreenshotRecorded, - bitmap); + _m_ReplayIntegration__onScreenshotRecorded, bitmap); return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jmethodID _m_ScreenshotRecorderCallback__onScreenshotRecorded1 = NULL; +jmethodID _m_ReplayIntegration__onScreenshotRecorded1 = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderCallback__onScreenshotRecorded1(jobject self_, - jobject file, - int64_t j) { +JniResult ReplayIntegration__onScreenshotRecorded1(jobject self_, + jobject file, + int64_t j) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderCallback, - "io/sentry/android/replay/ScreenshotRecorderCallback"); - if (_c_ScreenshotRecorderCallback == NULL) + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderCallback, - &_m_ScreenshotRecorderCallback__onScreenshotRecorded1, + load_method(_c_ReplayIntegration, + &_m_ReplayIntegration__onScreenshotRecorded1, "onScreenshotRecorded", "(Ljava/io/File;J)V"); - if (_m_ScreenshotRecorderCallback__onScreenshotRecorded1 == NULL) + if (_m_ReplayIntegration__onScreenshotRecorded1 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; (*jniEnv)->CallVoidMethod( - jniEnv, self_, _m_ScreenshotRecorderCallback__onScreenshotRecorded1, file, - j); + jniEnv, self_, _m_ReplayIntegration__onScreenshotRecorded1, file, j); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jmethodID _m_ReplayIntegration__close = NULL; +FFI_PLUGIN_EXPORT +JniResult ReplayIntegration__close(jobject self_) { + load_env(); + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__close, "close", + "()V"); + if (_m_ReplayIntegration__close == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__close); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jmethodID _m_ReplayIntegration__onConfigurationChanged = NULL; +FFI_PLUGIN_EXPORT +JniResult ReplayIntegration__onConfigurationChanged(jobject self_, + jobject configuration) { + load_env(); + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method( + _c_ReplayIntegration, &_m_ReplayIntegration__onConfigurationChanged, + "onConfigurationChanged", "(Landroid/content/res/Configuration;)V"); + if (_m_ReplayIntegration__onConfigurationChanged == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, + _m_ReplayIntegration__onConfigurationChanged, + configuration); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jmethodID _m_ReplayIntegration__onLowMemory = NULL; +FFI_PLUGIN_EXPORT +JniResult ReplayIntegration__onLowMemory(jobject self_) { + load_env(); + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__onLowMemory, + "onLowMemory", "()V"); + if (_m_ReplayIntegration__onLowMemory == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__onLowMemory); return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } @@ -552,60 +827,24 @@ JniResult SentryFlutterReplay__getCacheDir(jobject self_) { return to_global_ref_result(_result); } -jmethodID _m_SentryFlutterReplay__setCacheDir = NULL; -FFI_PLUGIN_EXPORT -JniResult SentryFlutterReplay__setCacheDir(jobject self_, jobject string) { - load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__setCacheDir, - "setCacheDir", "(Ljava/lang/String;)V"); - if (_m_SentryFlutterReplay__setCacheDir == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_SentryFlutterReplay__setCacheDir, - string); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jmethodID _m_SentryFlutterReplay__getCallback = NULL; +jmethodID _m_SentryFlutterReplay__getCallbackObject = NULL; FFI_PLUGIN_EXPORT -JniResult SentryFlutterReplay__getCallback(jobject self_) { +JniResult SentryFlutterReplay__getCallbackObject(jobject self_) { load_env(); load_class_global_ref(&_c_SentryFlutterReplay, "io/sentry/flutter/SentryFlutterReplay"); if (_c_SentryFlutterReplay == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__getCallback, - "getCallback", + load_method(_c_SentryFlutterReplay, + &_m_SentryFlutterReplay__getCallbackObject, "getCallbackObject", "()Lio/sentry/android/replay/ScreenshotRecorderCallback;"); - if (_m_SentryFlutterReplay__getCallback == NULL) + if (_m_SentryFlutterReplay__getCallbackObject == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_SentryFlutterReplay__getCallback); + jniEnv, self_, _m_SentryFlutterReplay__getCallbackObject); return to_global_ref_result(_result); } -jmethodID _m_SentryFlutterReplay__setCallback = NULL; -FFI_PLUGIN_EXPORT -JniResult SentryFlutterReplay__setCallback(jobject self_, - jobject screenshotRecorderCallback) { - load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__setCallback, - "setCallback", - "(Lio/sentry/android/replay/ScreenshotRecorderCallback;)V"); - if (_m_SentryFlutterReplay__setCallback == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_SentryFlutterReplay__setCallback, - screenshotRecorderCallback); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - jfieldID _f_SentryFlutterReplay__INSTANCE = NULL; FFI_PLUGIN_EXPORT JniResult get_SentryFlutterReplay__INSTANCE() { @@ -650,63 +889,34 @@ JniResult set_SentryFlutterReplay__recorder(jobject value) { return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jfieldID _f_SentryFlutterReplay__cacheDir = NULL; -FFI_PLUGIN_EXPORT -JniResult get_SentryFlutterReplay__cacheDir() { - load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__cacheDir, - "cacheDir", "Ljava/lang/String;"); - jobject _result = (*jniEnv)->GetStaticObjectField( - jniEnv, _c_SentryFlutterReplay, _f_SentryFlutterReplay__cacheDir); - return to_global_ref_result(_result); -} - -FFI_PLUGIN_EXPORT -JniResult set_SentryFlutterReplay__cacheDir(jobject value) { - load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__cacheDir, - "cacheDir", "Ljava/lang/String;"); - (*jniEnv)->SetStaticObjectField(jniEnv, _c_SentryFlutterReplay, - _f_SentryFlutterReplay__cacheDir, value); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jfieldID _f_SentryFlutterReplay__callback = NULL; +jfieldID _f_SentryFlutterReplay__integration = NULL; FFI_PLUGIN_EXPORT -JniResult get_SentryFlutterReplay__callback() { +JniResult get_SentryFlutterReplay__integration() { load_env(); load_class_global_ref(&_c_SentryFlutterReplay, "io/sentry/flutter/SentryFlutterReplay"); if (_c_SentryFlutterReplay == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__callback, - "callback", - "Lio/sentry/android/replay/ScreenshotRecorderCallback;"); + load_static_field(_c_SentryFlutterReplay, + &_f_SentryFlutterReplay__integration, "integration", + "Lio/sentry/android/replay/ReplayIntegration;"); jobject _result = (*jniEnv)->GetStaticObjectField( - jniEnv, _c_SentryFlutterReplay, _f_SentryFlutterReplay__callback); + jniEnv, _c_SentryFlutterReplay, _f_SentryFlutterReplay__integration); return to_global_ref_result(_result); } FFI_PLUGIN_EXPORT -JniResult set_SentryFlutterReplay__callback(jobject value) { +JniResult set_SentryFlutterReplay__integration(jobject value) { load_env(); load_class_global_ref(&_c_SentryFlutterReplay, "io/sentry/flutter/SentryFlutterReplay"); if (_c_SentryFlutterReplay == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__callback, - "callback", - "Lio/sentry/android/replay/ScreenshotRecorderCallback;"); + load_static_field(_c_SentryFlutterReplay, + &_f_SentryFlutterReplay__integration, "integration", + "Lio/sentry/android/replay/ReplayIntegration;"); (*jniEnv)->SetStaticObjectField(jniEnv, _c_SentryFlutterReplay, - _f_SentryFlutterReplay__callback, value); + _f_SentryFlutterReplay__integration, value); return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index a31ce77d3e..6c890bacf2 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -138,25 +138,18 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { options.integrations.removeAll { it is ReplayIntegration } val recorder = SentryFlutterReplay.recorder if (recorder != null && options.cacheDirPath != null) { - var replay = ReplayIntegration( + val replay = ReplayIntegration( context, dateProvider = CurrentDateProvider.getInstance(), recorderProvider = { recorder }, recorderConfigProvider = null, // TODO implement in dart replayCacheProvider = null - // TODO we only do this to have a cache dir path where to save stuff. - // replayCacheProvider = { replayId -> - // SentryFlutterReplay.replayCacheDir = File(options.cacheDirPath!!, "replay_$replayId") - // ReplayCache(options, replayId, recorderConfig) - // }, ) - // FIXME temporary - SentryFlutterReplay.cacheDir = File(options.cacheDirPath!!, "replay_0").path - options.addIntegration(replay) options.setReplayController(replay) - SentryFlutterReplay.callback = replay + + SentryFlutterReplay.integration = replay } else { options.setReplayController(null) } diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt index eb913412b0..4ee6fb2ede 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt @@ -2,14 +2,12 @@ package io.sentry.flutter import io.sentry.android.replay.Recorder import io.sentry.android.replay.ScreenshotRecorderCallback +import io.sentry.android.replay.ReplayIntegration object SentryFlutterReplay { // Set by the Flutter side, read during SentryAndroid.init() lateinit var recorder: Recorder // Set by SentryAndroid.init(), read by the Flutter side in recorder.start() - lateinit var cacheDir: String - - // Set by SentryAndroid.init(), read by the Flutter side in recorder.start() - lateinit var callback: ScreenshotRecorderCallback + lateinit var integration: ReplayIntegration } diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index c7f202f4f9..5f969d9ac1 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -90,6 +90,8 @@ Future setupSentry( options.maxResponseBodySize = MaxResponseBodySize.always; options.navigatorKey = navigatorKey; + options.replay.sessionSampleRate = 1.0; + _isIntegrationTest = isIntegrationTest; if (_isIntegrationTest) { options.dist = '1'; diff --git a/flutter/ffi-jni.yaml b/flutter/ffi-jni.yaml index 14c17958b0..e610fa69e7 100644 --- a/flutter/ffi-jni.yaml +++ b/flutter/ffi-jni.yaml @@ -18,7 +18,7 @@ log_level: all classes: - io.sentry.android.replay.Recorder - io.sentry.android.replay.ScreenshotRecorderConfig - - io.sentry.android.replay.ScreenshotRecorderCallback + - io.sentry.android.replay.ReplayIntegration - io.sentry.flutter.SentryFlutterReplay - java.io.File diff --git a/flutter/lib/src/native/java/android_replay_recorder.dart b/flutter/lib/src/native/java/android_replay_recorder.dart index 951e7bab7b..a8451e60ea 100644 --- a/flutter/lib/src/native/java/android_replay_recorder.dart +++ b/flutter/lib/src/native/java/android_replay_recorder.dart @@ -12,7 +12,7 @@ import 'binding.dart' as java; @internal class AndroidReplayRecorder implements java.$RecorderImpl { late ScreenshotRecorder _recorder; - late java.ScreenshotRecorderCallback? _callback; + late java.ReplayIntegration? _integration; AndroidReplayRecorder._(); @@ -27,9 +27,13 @@ class AndroidReplayRecorder implements java.$RecorderImpl { @override void start(java.ScreenshotRecorderConfig config) { + _integration = java.SentryFlutterReplay.integration; + + var jniCacheDir = _integration!.getReplayCacheDir(); var cacheDir = - java.SentryFlutterReplay.cacheDir.toDartString(releaseOriginal: true); - _callback = java.SentryFlutterReplay.callback; + jniCacheDir.getAbsolutePath().toDartString(releaseOriginal: true); + jniCacheDir.release(); + _recorder = ScreenshotRecorder( ScreenshotRecorderConfig( config.getRecordingWidth(), @@ -46,7 +50,7 @@ class AndroidReplayRecorder implements java.$RecorderImpl { var jFilePath = filePath.toJString(); var jFile = java.File(jFilePath); try { - _callback?.onScreenshotRecorded1(jFile, timestamp); + _integration?.onScreenshotRecorded1(jFile, timestamp); } finally { jFile.release(); jFilePath.release(); @@ -59,8 +63,8 @@ class AndroidReplayRecorder implements java.$RecorderImpl { @override void stop() { _recorder.stop(); - var callback = _callback; - _callback = null; - callback?.release(); + var integration = _integration; + _integration = null; + integration?.release(); } } diff --git a/flutter/lib/src/native/java/binding.dart b/flutter/lib/src/native/java/binding.dart index d4718ebe11..19625de333 100644 --- a/flutter/lib/src/native/java/binding.dart +++ b/flutter/lib/src/native/java/binding.dart @@ -583,27 +583,237 @@ class $ScreenshotRecorderConfigType } } -/// from: io.sentry.android.replay.ScreenshotRecorderCallback -class ScreenshotRecorderCallback extends jni.JObject { +/// from: io.sentry.android.replay.ReplayIntegration +class ReplayIntegration extends jni.JObject { @override - late final jni.JObjType $type = type; + late final jni.JObjType $type = type; - ScreenshotRecorderCallback.fromRef( + ReplayIntegration.fromRef( jni.JObjectPtr ref, ) : super.fromRef(ref); /// The type which includes information such as the signature of this class. - static const type = $ScreenshotRecorderCallbackType(); + static const type = $ReplayIntegrationType(); + static final _new0 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>>("ReplayIntegration__new0") + .asFunction< + jni.JniResult Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>(); + + /// from: public void (android.content.Context context, io.sentry.transport.ICurrentDateProvider iCurrentDateProvider, kotlin.jvm.functions.Function0 function0, kotlin.jvm.functions.Function1 function1, kotlin.jvm.functions.Function1 function11) + /// The returned object must be released after use, by calling the [release] method. + factory ReplayIntegration( + jni.JObject context, + jni.JObject iCurrentDateProvider, + jni.JObject function0, + jni.JObject function1, + jni.JObject function11, + ) { + return ReplayIntegration.fromRef(_new0( + context.reference, + iCurrentDateProvider.reference, + function0.reference, + function1.reference, + function11.reference) + .object); + } + + static final _new1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Int32, + ffi.Pointer)>>("ReplayIntegration__new1") + .asFunction< + jni.JniResult Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + int, + ffi.Pointer)>(); + + /// from: public void (android.content.Context context, io.sentry.transport.ICurrentDateProvider iCurrentDateProvider, kotlin.jvm.functions.Function0 function0, kotlin.jvm.functions.Function1 function1, kotlin.jvm.functions.Function1 function11, int i, kotlin.jvm.internal.DefaultConstructorMarker defaultConstructorMarker) + /// The returned object must be released after use, by calling the [release] method. + factory ReplayIntegration.new1( + jni.JObject context, + jni.JObject iCurrentDateProvider, + jni.JObject function0, + jni.JObject function1, + jni.JObject function11, + int i, + jni.JObject defaultConstructorMarker, + ) { + return ReplayIntegration.fromRef(_new1( + context.reference, + iCurrentDateProvider.reference, + function0.reference, + function1.reference, + function11.reference, + i, + defaultConstructorMarker.reference) + .object); + } + + static final _new2 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("ReplayIntegration__new2") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public void (android.content.Context context, io.sentry.transport.ICurrentDateProvider iCurrentDateProvider) + /// The returned object must be released after use, by calling the [release] method. + factory ReplayIntegration.new2( + jni.JObject context, + jni.JObject iCurrentDateProvider, + ) { + return ReplayIntegration.fromRef( + _new2(context.reference, iCurrentDateProvider.reference).object); + } + + static final _getReplayCacheDir = jniLookup< + ffi + .NativeFunction)>>( + "ReplayIntegration__getReplayCacheDir") + .asFunction)>(); + + /// from: public final java.io.File getReplayCacheDir() + /// The returned object must be released after use, by calling the [release] method. + File getReplayCacheDir() { + return const $FileType().fromRef(_getReplayCacheDir(reference).object); + } + + static final _register = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>>("ReplayIntegration__register") + .asFunction< + jni.JniResult Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + /// from: public void register(io.sentry.IHub iHub, io.sentry.SentryOptions sentryOptions) + void register( + jni.JObject iHub, + jni.JObject sentryOptions, + ) { + return _register(reference, iHub.reference, sentryOptions.reference) + .check(); + } + + static final _isRecording = jniLookup< + ffi + .NativeFunction)>>( + "ReplayIntegration__isRecording") + .asFunction)>(); + + /// from: public boolean isRecording() + bool isRecording() { + return _isRecording(reference).boolean; + } + + static final _start = jniLookup< + ffi + .NativeFunction)>>( + "ReplayIntegration__start") + .asFunction)>(); + + /// from: public void start() + void start() { + return _start(reference).check(); + } + + static final _resume = jniLookup< + ffi + .NativeFunction)>>( + "ReplayIntegration__resume") + .asFunction)>(); + + /// from: public void resume() + void resume() { + return _resume(reference).check(); + } + + static final _sendReplayForEvent = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer, ffi.Pointer)>>( + "ReplayIntegration__sendReplayForEvent") + .asFunction< + jni.JniResult Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + /// from: public void sendReplayForEvent(io.sentry.SentryEvent sentryEvent, io.sentry.Hint hint) + void sendReplayForEvent( + jni.JObject sentryEvent, + jni.JObject hint, + ) { + return _sendReplayForEvent(reference, sentryEvent.reference, hint.reference) + .check(); + } + + static final _getReplayId = jniLookup< + ffi + .NativeFunction)>>( + "ReplayIntegration__getReplayId") + .asFunction)>(); + + /// from: public io.sentry.protocol.SentryId getReplayId() + /// The returned object must be released after use, by calling the [release] method. + jni.JObject getReplayId() { + return const jni.JObjectType().fromRef(_getReplayId(reference).object); + } + + static final _pause = jniLookup< + ffi + .NativeFunction)>>( + "ReplayIntegration__pause") + .asFunction)>(); + + /// from: public void pause() + void pause() { + return _pause(reference).check(); + } + + static final _stop = jniLookup< + ffi + .NativeFunction)>>( + "ReplayIntegration__stop") + .asFunction)>(); + + /// from: public void stop() + void stop() { + return _stop(reference).check(); + } + static final _onScreenshotRecorded = jniLookup< ffi.NativeFunction< jni.JniResult Function( ffi.Pointer, ffi.Pointer)>>( - "ScreenshotRecorderCallback__onScreenshotRecorded") + "ReplayIntegration__onScreenshotRecorded") .asFunction< jni.JniResult Function( ffi.Pointer, ffi.Pointer)>(); - /// from: public abstract void onScreenshotRecorded(android.graphics.Bitmap bitmap) + /// from: public void onScreenshotRecorded(android.graphics.Bitmap bitmap) void onScreenshotRecorded( jni.JObject bitmap, ) { @@ -611,15 +821,16 @@ class ScreenshotRecorderCallback extends jni.JObject { } static final _onScreenshotRecorded1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer, ffi.Int64)>>( - "ScreenshotRecorderCallback__onScreenshotRecorded1") + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, + ffi.Pointer, + ffi.Int64)>>("ReplayIntegration__onScreenshotRecorded1") .asFunction< jni.JniResult Function( ffi.Pointer, ffi.Pointer, int)>(); - /// from: public abstract void onScreenshotRecorded(java.io.File file, long j) + /// from: public void onScreenshotRecorded(java.io.File file, long j) void onScreenshotRecorded1( File file, int j, @@ -627,127 +838,54 @@ class ScreenshotRecorderCallback extends jni.JObject { return _onScreenshotRecorded1(reference, file.reference, j).check(); } - /// Maps a specific port to the implemented interface. - static final Map _$impls = {}; - ReceivePort? _$p; + static final _close = jniLookup< + ffi + .NativeFunction)>>( + "ReplayIntegration__close") + .asFunction)>(); - static jni.JObjectPtr _$invoke( - int port, - jni.JObjectPtr descriptor, - jni.JObjectPtr args, - ) { - return _$invokeMethod( - port, - $MethodInvocation.fromAddresses( - 0, - descriptor.address, - args.address, - ), - ); + /// from: public void close() + void close() { + return _close(reference).check(); } - static final ffi.Pointer< - ffi.NativeFunction< - jni.JObjectPtr Function( - ffi.Uint64, jni.JObjectPtr, jni.JObjectPtr)>> - _$invokePointer = ffi.Pointer.fromFunction(_$invoke); - - static ffi.Pointer _$invokeMethod( - int $p, - $MethodInvocation $i, - ) { - try { - final $d = $i.methodDescriptor.toDartString(releaseOriginal: true); - final $a = $i.args; - if ($d == r"onScreenshotRecorded(Landroid/graphics/Bitmap;)V") { - _$impls[$p]!.onScreenshotRecorded( - $a[0].castTo(const jni.JObjectType(), releaseOriginal: true), - ); - return jni.nullptr; - } - if ($d == r"onScreenshotRecorded(Ljava/io/File;J)V") { - _$impls[$p]!.onScreenshotRecorded1( - $a[0].castTo(const $FileType(), releaseOriginal: true), - $a[1] - .castTo(const jni.JLongType(), releaseOriginal: true) - .longValue(releaseOriginal: true), - ); - return jni.nullptr; - } - } catch (e) { - return ProtectedJniExtensions.newDartException(e.toString()); - } - return jni.nullptr; - } + static final _onConfigurationChanged = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>>( + "ReplayIntegration__onConfigurationChanged") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); - factory ScreenshotRecorderCallback.implement( - $ScreenshotRecorderCallbackImpl $impl, + /// from: public void onConfigurationChanged(android.content.res.Configuration configuration) + void onConfigurationChanged( + jni.JObject configuration, ) { - final $p = ReceivePort(); - final $x = ScreenshotRecorderCallback.fromRef( - ProtectedJniExtensions.newPortProxy( - r"io.sentry.android.replay.ScreenshotRecorderCallback", - $p, - _$invokePointer, - ), - ).._$p = $p; - final $a = $p.sendPort.nativePort; - _$impls[$a] = $impl; - $p.listen(($m) { - if ($m == null) { - _$impls.remove($p.sendPort.nativePort); - $p.close(); - return; - } - final $i = $MethodInvocation.fromMessage($m); - final $r = _$invokeMethod($p.sendPort.nativePort, $i); - ProtectedJniExtensions.returnResult($i.result, $r); - }); - return $x; + return _onConfigurationChanged(reference, configuration.reference).check(); } -} - -abstract class $ScreenshotRecorderCallbackImpl { - factory $ScreenshotRecorderCallbackImpl({ - required void Function(jni.JObject bitmap) onScreenshotRecorded, - required void Function(File file, int j) onScreenshotRecorded1, - }) = _$ScreenshotRecorderCallbackImpl; - - void onScreenshotRecorded(jni.JObject bitmap); - void onScreenshotRecorded1(File file, int j); -} - -class _$ScreenshotRecorderCallbackImpl - implements $ScreenshotRecorderCallbackImpl { - _$ScreenshotRecorderCallbackImpl({ - required void Function(jni.JObject bitmap) onScreenshotRecorded, - required void Function(File file, int j) onScreenshotRecorded1, - }) : _onScreenshotRecorded = onScreenshotRecorded, - _onScreenshotRecorded1 = onScreenshotRecorded1; - final void Function(jni.JObject bitmap) _onScreenshotRecorded; - final void Function(File file, int j) _onScreenshotRecorded1; - - void onScreenshotRecorded(jni.JObject bitmap) { - return _onScreenshotRecorded(bitmap); - } + static final _onLowMemory = jniLookup< + ffi + .NativeFunction)>>( + "ReplayIntegration__onLowMemory") + .asFunction)>(); - void onScreenshotRecorded1(File file, int j) { - return _onScreenshotRecorded1(file, j); + /// from: public void onLowMemory() + void onLowMemory() { + return _onLowMemory(reference).check(); } } -class $ScreenshotRecorderCallbackType - extends jni.JObjType { - const $ScreenshotRecorderCallbackType(); +class $ReplayIntegrationType extends jni.JObjType { + const $ReplayIntegrationType(); @override - String get signature => - r"Lio/sentry/android/replay/ScreenshotRecorderCallback;"; + String get signature => r"Lio/sentry/android/replay/ReplayIntegration;"; @override - ScreenshotRecorderCallback fromRef(jni.JObjectPtr ref) => - ScreenshotRecorderCallback.fromRef(ref); + ReplayIntegration fromRef(jni.JObjectPtr ref) => + ReplayIntegration.fromRef(ref); @override jni.JObjType get superType => const jni.JObjectType(); @@ -756,12 +894,12 @@ class $ScreenshotRecorderCallbackType final superCount = 1; @override - int get hashCode => ($ScreenshotRecorderCallbackType).hashCode; + int get hashCode => ($ReplayIntegrationType).hashCode; @override bool operator ==(Object other) { - return other.runtimeType == ($ScreenshotRecorderCallbackType) && - other is $ScreenshotRecorderCallbackType; + return other.runtimeType == ($ReplayIntegrationType) && + other is $ReplayIntegrationType; } } @@ -806,47 +944,26 @@ class SentryFlutterReplay extends jni.JObject { /// The returned object must be released after use, by calling the [release] method. static set recorder(Recorder value) => _set_recorder(value.reference).check(); - static final _get_cacheDir = - jniLookup>( - "get_SentryFlutterReplay__cacheDir") - .asFunction(); - - static final _set_cacheDir = jniLookup< - ffi - .NativeFunction)>>( - "set_SentryFlutterReplay__cacheDir") - .asFunction)>(); - - /// from: static public java.lang.String cacheDir - /// The returned object must be released after use, by calling the [release] method. - static jni.JString get cacheDir => - const jni.JStringType().fromRef(_get_cacheDir().object); - - /// from: static public java.lang.String cacheDir - /// The returned object must be released after use, by calling the [release] method. - static set cacheDir(jni.JString value) => - _set_cacheDir(value.reference).check(); - - static final _get_callback = + static final _get_integration = jniLookup>( - "get_SentryFlutterReplay__callback") + "get_SentryFlutterReplay__integration") .asFunction(); - static final _set_callback = jniLookup< + static final _set_integration = jniLookup< ffi .NativeFunction)>>( - "set_SentryFlutterReplay__callback") + "set_SentryFlutterReplay__integration") .asFunction)>(); - /// from: static public io.sentry.android.replay.ScreenshotRecorderCallback callback + /// from: static public io.sentry.android.replay.ReplayIntegration integration /// The returned object must be released after use, by calling the [release] method. - static ScreenshotRecorderCallback get callback => - const $ScreenshotRecorderCallbackType().fromRef(_get_callback().object); + static ReplayIntegration get integration => + const $ReplayIntegrationType().fromRef(_get_integration().object); - /// from: static public io.sentry.android.replay.ScreenshotRecorderCallback callback + /// from: static public io.sentry.android.replay.ReplayIntegration integration /// The returned object must be released after use, by calling the [release] method. - static set callback(ScreenshotRecorderCallback value) => - _set_callback(value.reference).check(); + static set integration(ReplayIntegration value) => + _set_integration(value.reference).check(); static final _getRecorder = jniLookup< ffi @@ -887,48 +1004,17 @@ class SentryFlutterReplay extends jni.JObject { return const jni.JStringType().fromRef(_getCacheDir(reference).object); } - static final _setCacheDir = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("SentryFlutterReplay__setCacheDir") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public final void setCacheDir(java.lang.String string) - void setCacheDir( - jni.JString string, - ) { - return _setCacheDir(reference, string.reference).check(); - } - - static final _getCallback = jniLookup< + static final _getCallbackObject = jniLookup< ffi .NativeFunction)>>( - "SentryFlutterReplay__getCallback") + "SentryFlutterReplay__getCallbackObject") .asFunction)>(); - /// from: public final io.sentry.android.replay.ScreenshotRecorderCallback getCallback() + /// from: public final io.sentry.android.replay.ScreenshotRecorderCallback getCallbackObject() /// The returned object must be released after use, by calling the [release] method. - ScreenshotRecorderCallback getCallback() { - return const $ScreenshotRecorderCallbackType() - .fromRef(_getCallback(reference).object); - } - - static final _setCallback = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("SentryFlutterReplay__setCallback") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public final void setCallback(io.sentry.android.replay.ScreenshotRecorderCallback screenshotRecorderCallback) - void setCallback( - ScreenshotRecorderCallback screenshotRecorderCallback, - ) { - return _setCallback(reference, screenshotRecorderCallback.reference) - .check(); + jni.JObject getCallbackObject() { + return const jni.JObjectType() + .fromRef(_getCallbackObject(reference).object); } } From afa9f50cb7de6f1c4766fc2f8d4e738c19ccc69d Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 23 Apr 2024 14:17:23 +0200 Subject: [PATCH 16/74] chore: update to cocoa 8.24.1-alpha.0 --- flutter/ios/sentry_flutter.podspec | 2 +- flutter/scripts/update-cocoa.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter/ios/sentry_flutter.podspec b/flutter/ios/sentry_flutter.podspec index f67b35e2e4..cb2f041a6f 100644 --- a/flutter/ios/sentry_flutter.podspec +++ b/flutter/ios/sentry_flutter.podspec @@ -16,7 +16,7 @@ Sentry SDK for Flutter with support to native through sentry-cocoa. :tag => s.version.to_s } s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' - s.dependency 'Sentry/HybridSDK', '8.24.0' + s.dependency 'Sentry/HybridSDK', '8.24.1-alpha.0' s.ios.dependency 'Flutter' s.osx.dependency 'FlutterMacOS' s.ios.deployment_target = '12.0' diff --git a/flutter/scripts/update-cocoa.sh b/flutter/scripts/update-cocoa.sh index d54a754399..55f1c854db 100755 --- a/flutter/scripts/update-cocoa.sh +++ b/flutter/scripts/update-cocoa.sh @@ -4,7 +4,7 @@ set -euo pipefail cd $(dirname "$0")/../ios file='sentry_flutter.podspec' content=$(cat $file) -regex="('Sentry/HybridSDK', *)'([0-9\.]+)'" +regex="('Sentry/HybridSDK', *)'([0-9\.]+(\-[a-z0-9\.]+)?)'" if ! [[ $content =~ $regex ]]; then echo "Failed to find the plugin version in $file" exit 1 From 3df25235385dfb03295b9f28c6ef2be5c90b3d63 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 24 Apr 2024 11:07:14 +0200 Subject: [PATCH 17/74] wip: cocoa integration --- .../Classes/SentryFlutterPluginApple.swift | 26 ++++++++++++++----- .../SentryFlutterReplayIntegration.swift | 14 ++++++++++ 2 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 flutter/ios/Classes/SentryFlutterReplayIntegration.swift diff --git a/flutter/ios/Classes/SentryFlutterPluginApple.swift b/flutter/ios/Classes/SentryFlutterPluginApple.swift index ec6eb7cdb7..92160153c2 100644 --- a/flutter/ios/Classes/SentryFlutterPluginApple.swift +++ b/flutter/ios/Classes/SentryFlutterPluginApple.swift @@ -264,9 +264,9 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin { if arguments["enableAutoPerformanceTracing"] as? Bool ?? false { PrivateSentrySDKOnly.appStartMeasurementHybridSDKMode = true - #if os(iOS) || targetEnvironment(macCatalyst) +#if os(iOS) || targetEnvironment(macCatalyst) PrivateSentrySDKOnly.framesTrackingMeasurementHybridSDKMode = true - #endif +#endif } let version = PrivateSentrySDKOnly.getSdkVersionString() @@ -297,17 +297,29 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin { return event } + + // Remove the default replay integration, we'll replace it with a custom, flutter-specific one. + var integrations = options.integrations!.filter { (name) -> Bool in + return name != "SentrySessionReplayIntegration" + } + integrations.append(String(describing: SentryFlutterReplayIntegration.self)) + options.integrations = integrations } - if didReceiveDidBecomeActiveNotification && + // let hub = PrivateSentrySDKOnly.inst + // var replayIntegration = SentryFlutterReplayIntegration() + // if (replayIntegration.install(with: hub.)) + // SentrySDK.currentHub().addInstalledIntegration(SentryIntegrationProtocol, name: <#T##String#>) + + if didReceiveDidBecomeActiveNotification && (PrivateSentrySDKOnly.options.enableAutoSessionTracking || PrivateSentrySDKOnly.options.enableWatchdogTerminationTracking) { // We send a SentryHybridSdkDidBecomeActive to the Sentry Cocoa SDK, so the SDK will mimics // the didBecomeActiveNotification notification. This is needed for session and OOM tracking. - NotificationCenter.default.post(name: Notification.Name("SentryHybridSdkDidBecomeActive"), object: nil) - // we reset the flag for the sake of correctness - didReceiveDidBecomeActiveNotification = false - } + NotificationCenter.default.post(name: Notification.Name("SentryHybridSdkDidBecomeActive"), object: nil) + // we reset the flag for the sake of correctness + didReceiveDidBecomeActiveNotification = false + } result("") } diff --git a/flutter/ios/Classes/SentryFlutterReplayIntegration.swift b/flutter/ios/Classes/SentryFlutterReplayIntegration.swift new file mode 100644 index 0000000000..83c33f7c7c --- /dev/null +++ b/flutter/ios/Classes/SentryFlutterReplayIntegration.swift @@ -0,0 +1,14 @@ +import Foundation +import Sentry + +@objc +class SentryFlutterReplayIntegration: NSObject, SentryIntegrationProtocol { + func install(with options: Options) -> Bool { + print("Installing flutter replay integration") + return true + } + + func uninstall() { + + } +} From 179b5d4d6da2930042c1c0a347b86e5c2c4be4f9 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 24 Apr 2024 13:25:47 +0200 Subject: [PATCH 18/74] wip: ios replay --- .../Classes/SentryFlutterPluginApple.swift | 7 +-- .../SentryFlutterReplayIntegration.swift | 59 ++++++++++++++++++- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/flutter/ios/Classes/SentryFlutterPluginApple.swift b/flutter/ios/Classes/SentryFlutterPluginApple.swift index 92160153c2..51b5473f6e 100644 --- a/flutter/ios/Classes/SentryFlutterPluginApple.swift +++ b/flutter/ios/Classes/SentryFlutterPluginApple.swift @@ -302,15 +302,10 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin { var integrations = options.integrations!.filter { (name) -> Bool in return name != "SentrySessionReplayIntegration" } - integrations.append(String(describing: SentryFlutterReplayIntegration.self)) + integrations.append(NSStringFromClass(SentryFlutterReplayIntegration.self)) options.integrations = integrations } - // let hub = PrivateSentrySDKOnly.inst - // var replayIntegration = SentryFlutterReplayIntegration() - // if (replayIntegration.install(with: hub.)) - // SentrySDK.currentHub().addInstalledIntegration(SentryIntegrationProtocol, name: <#T##String#>) - if didReceiveDidBecomeActiveNotification && (PrivateSentrySDKOnly.options.enableAutoSessionTracking || PrivateSentrySDKOnly.options.enableWatchdogTerminationTracking) { diff --git a/flutter/ios/Classes/SentryFlutterReplayIntegration.swift b/flutter/ios/Classes/SentryFlutterReplayIntegration.swift index 83c33f7c7c..6969999a78 100644 --- a/flutter/ios/Classes/SentryFlutterReplayIntegration.swift +++ b/flutter/ios/Classes/SentryFlutterReplayIntegration.swift @@ -3,12 +3,65 @@ import Sentry @objc class SentryFlutterReplayIntegration: NSObject, SentryIntegrationProtocol { + // private var sessionReplay: SentrySessionReplay = null + func install(with options: Options) -> Bool { - print("Installing flutter replay integration") - return true + // if #available(iOS 16.0, tvOS 16.0, *) { + // guard let replayOptions = options.experimental.sessionReplay else { + // return false + // } + + // let shouldReplayFullSession = SentryDependencyContainer.sharedInstance.random.nextNumber() < replayOptions.sessionSampleRate + // if !shouldReplayFullSession && replayOptions.errorSampleRate == 0 { + // return false + // } + + // let replayDir = URL(fileURLWithPath: SentryDependencyContainer.sharedInstance.fileManager.sentryPath) + // .appendingPathComponent(SENTRY_REPLAY_FOLDER) + // .appendingPathComponent(UUID().uuidString) + + // if !FileManager.default.fileExists(atPath: replayDir.path) { + // try? FileManager.default.createDirectory(at: replayDir, withIntermediateDirectories: true, attributes: nil) + // } + + // let replayMaker = SentryOnDemandReplay(outputPath: replayDir.path) + // replayMaker.bitRate = replayOptions.replayBitRate + // replayMaker.cacheMaxSize = shouldReplayFullSession ? replayOptions.sessionSegmentDuration : replayOptions.errorReplayDuration + + // self.sessionReplay = SentrySessionReplay( + // settings: replayOptions, + // replayFolderPath: replayDir, + // screenshotProvider: SentryViewPhotographer.shared, + // replayMaker: replayMaker, + // dateProvider: SentryDependencyContainer.sharedInstance.dateProvider, + // random: SentryDependencyContainer.sharedInstance.random, + // displayLinkWrapper: SentryDisplayLinkWrapper() + // ) + + // self.sessionReplay.start( + // SentryDependencyContainer.sharedInstance.application.windows.first, + // fullSession: shouldReplayFullSession + // ) + + // NotificationCenter.default.addObserver( + // self, + // selector: #selector(stop), + // name: UIApplication.didEnterBackgroundNotification, + // object: nil + // ) + + // SentryGlobalEventProcessor.shared.addEventProcessor { event in + // self.sessionReplay.captureReplay(for: event) + // return event + // } + + // return true + // } else { + return false + // } } func uninstall() { - + // self.sessionReplay.stop() } } From 6432b98a1e687631548b609455835866e1dfc852 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 25 Apr 2024 14:53:44 +0200 Subject: [PATCH 19/74] cleanup --- .../kotlin/io/sentry/flutter/SentryFlutter.kt | 17 -------------- .../io/sentry/flutter/SentryFlutterTest.kt | 13 ++--------- .../integrations/native_sdk_integration.dart | 7 ------ .../native/java/android_replay_recorder.dart | 19 +++++++++------- flutter/lib/src/replay/recorder_config.dart | 4 +--- flutter/lib/src/sentry_replay_options.dart | 22 ------------------- 6 files changed, 14 insertions(+), 68 deletions(-) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutter.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutter.kt index 45e7f129e4..2d57716208 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutter.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutter.kt @@ -129,23 +129,6 @@ class SentryFlutter( fun updateReplayOptions(options: SentryReplayOptions, data: Map) { options.sessionSampleRate = data["sessionSampleRate"] as? Double options.errorSampleRate = data["errorSampleRate"] as? Double - - // Currently, these are read-only options - // data.getIfNotNull("bitRate") { - // options.bitRate = it - // } - // data.getIfNotNull("frameRate") { - // options.frameRate = it - // } - // data.getIfNotNull("errorReplayDurationMillis") { - // options.errorReplayDuration = it - // } - // data.getIfNotNull("sessionSegmentDurationMillis") { - // options.replayCacheDefaultLowerBound = it - // } - // data.getIfNotNull("sessionDurationMillis") { - // options.sessionDuration = it - // } } } diff --git a/flutter/android/src/test/kotlin/io/sentry/flutter/SentryFlutterTest.kt b/flutter/android/src/test/kotlin/io/sentry/flutter/SentryFlutterTest.kt index 4082f4948c..6be5659456 100644 --- a/flutter/android/src/test/kotlin/io/sentry/flutter/SentryFlutterTest.kt +++ b/flutter/android/src/test/kotlin/io/sentry/flutter/SentryFlutterTest.kt @@ -64,12 +64,8 @@ class SentryFlutterTest { assertEquals(0.5, fixture.options.experimental.sessionReplay.sessionSampleRate) assertEquals(0.6, fixture.options.experimental.sessionReplay.errorSampleRate) - // TODO, these are currently read-only in SentryReplayOptions so we're testing the default values instead. - // assertEquals(10, fixture.options.experimental.sessionReplay.bitRate) - // assertEquals(20, fixture.options.experimental.sessionReplay.frameRate) - // assertEquals(1000L, fixture.options.experimental.sessionReplay.errorReplayDuration) - // assertEquals(500L, fixture.options.experimental.sessionReplay.sessionSegmentDuration) - // assertEquals(10_000L, fixture.options.experimental.sessionReplay.sessionDuration) + // Note: these are currently read-only in SentryReplayOptions so we're only asserting the default values here to + // know when there's a change in the native SDK, as it may require a manual change in the Flutter implementation. assertEquals(100_000, fixture.options.experimental.sessionReplay.bitRate) assertEquals(1, fixture.options.experimental.sessionReplay.frameRate) assertEquals(30_000L, fixture.options.experimental.sessionReplay.errorReplayDuration) @@ -144,11 +140,6 @@ class Fixture { "replay" to mapOf( "sessionSampleRate" to 0.5, "errorSampleRate" to 0.6, - "bitRate" to 10, - "frameRate" to 20, - "errorReplayDurationMillis" to 1000L, - "sessionSegmentDurationMillis" to 500L, - "sessionDurationMillis" to 10_000L, ) ) diff --git a/flutter/lib/src/integrations/native_sdk_integration.dart b/flutter/lib/src/integrations/native_sdk_integration.dart index 1449320989..fe48c5b02e 100644 --- a/flutter/lib/src/integrations/native_sdk_integration.dart +++ b/flutter/lib/src/integrations/native_sdk_integration.dart @@ -57,13 +57,6 @@ class NativeSdkIntegration implements Integration { 'replay': { 'sessionSampleRate': options.replay.sessionSampleRate, 'errorSampleRate': options.replay.errorSampleRate, - 'bitRate': options.replay.bitRate, - 'frameRate': options.replay.frameRate, - 'errorReplayDurationMillis': - options.replay.errorReplayDuration.inMilliseconds, - 'sessionSegmentDurationMillis': - options.replay.sessionSegmentDuration.inMilliseconds, - 'sessionDuration': options.replay.sessionDuration.inMilliseconds }, }); diff --git a/flutter/lib/src/native/java/android_replay_recorder.dart b/flutter/lib/src/native/java/android_replay_recorder.dart index a8451e60ea..39f21c4e41 100644 --- a/flutter/lib/src/native/java/android_replay_recorder.dart +++ b/flutter/lib/src/native/java/android_replay_recorder.dart @@ -34,13 +34,7 @@ class AndroidReplayRecorder implements java.$RecorderImpl { jniCacheDir.getAbsolutePath().toDartString(releaseOriginal: true); jniCacheDir.release(); - _recorder = ScreenshotRecorder( - ScreenshotRecorderConfig( - config.getRecordingWidth(), - config.getRecordingHeight(), - config.getFrameRate(), - config.getBitRate(), - ), (image) async { + ScreenshotRecorderCallback callback = (image) async { var imageData = await image.toByteData(format: ImageByteFormat.png); if (imageData != null) { var timestamp = DateTime.now().millisecondsSinceEpoch; @@ -56,7 +50,16 @@ class AndroidReplayRecorder implements java.$RecorderImpl { jFilePath.release(); } } - }); + }; + + _recorder = ScreenshotRecorder( + ScreenshotRecorderConfig( + config.getRecordingWidth(), + config.getRecordingHeight(), + config.getFrameRate(), + ), + callback, + ); _recorder.start(); } diff --git a/flutter/lib/src/replay/recorder_config.dart b/flutter/lib/src/replay/recorder_config.dart index 1ac7de7995..cb66b30fed 100644 --- a/flutter/lib/src/replay/recorder_config.dart +++ b/flutter/lib/src/replay/recorder_config.dart @@ -5,8 +5,6 @@ class ScreenshotRecorderConfig { final int width; final int height; final int frameRate; - final int bitRate; - ScreenshotRecorderConfig( - this.width, this.height, this.frameRate, this.bitRate); + ScreenshotRecorderConfig(this.width, this.height, this.frameRate); } diff --git a/flutter/lib/src/sentry_replay_options.dart b/flutter/lib/src/sentry_replay_options.dart index 93c7425713..0035be40a5 100644 --- a/flutter/lib/src/sentry_replay_options.dart +++ b/flutter/lib/src/sentry_replay_options.dart @@ -46,28 +46,6 @@ class SentryReplayOptions { // /// Default is enabled. // bool redactAllImages = true; - /// Defines the quality of the session replay. Higher bit rates have better replay quality, but - /// also affect the final payload size to transfer, defaults to 100kbps. - @internal - int bitRate = 100000; - - /// Number of frames per second of the replay. The bigger the number, the more accurate the replay - /// will be, but also more data to transfer and more CPU load, defaults to 1fps. - @internal - int frameRate = 1; - - /// The maximum duration of replays for error events. - @internal - Duration errorReplayDuration = Duration(seconds: 30); - - /// The maximum duration of the segment of a session replay, defaults to 5s. - @internal - Duration sessionSegmentDuration = Duration(seconds: 5); - - /// The maximum duration of a full session replay, defaults to 1h. - @internal - Duration sessionDuration = Duration(hours: 1); - @internal bool get isEnabled => ((sessionSampleRate ?? 0) > 0) || ((errorSampleRate ?? 0) > 0); From 25fd69067e37cdc23b6853a7835177346c9e923c Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 25 Apr 2024 15:08:29 +0200 Subject: [PATCH 20/74] formatting --- .../kotlin/io/sentry/flutter/SentryFlutter.kt | 14 ++--- .../io/sentry/flutter/SentryFlutterPlugin.kt | 57 ++++++++++--------- .../io/sentry/flutter/SentryFlutterReplay.kt | 1 - 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutter.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutter.kt index 2d57716208..4aff29fc9b 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutter.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutter.kt @@ -1,21 +1,21 @@ package io.sentry.flutter import io.sentry.SentryLevel +import io.sentry.SentryReplayOptions import io.sentry.android.core.BuildConfig import io.sentry.android.core.SentryAndroidOptions -import io.sentry.SentryReplayOptions import io.sentry.protocol.SdkVersion import java.util.Locale class SentryFlutter( - private val androidSdk: String, - private val nativeSdk: String, + private val androidSdk: String, + private val nativeSdk: String, ) { var autoPerformanceTracingEnabled = false fun updateOptions( - options: SentryAndroidOptions, - data: Map, + options: SentryAndroidOptions, + data: Map, ) { data.getIfNotNull("dsn") { options.dsn = it @@ -135,8 +135,8 @@ class SentryFlutter( // Call the `completion` closure if cast to map value with `key` and type `T` is successful. @Suppress("UNCHECKED_CAST") private fun Map.getIfNotNull( - key: String, - callback: (T) -> Unit, + key: String, + callback: (T) -> Unit, ) { (get(key) as? T)?.let { callback(it) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index 429230643c..f6df64890d 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -45,10 +45,10 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { channel.setMethodCallHandler(this) sentryFlutter = - SentryFlutter( - androidSdk = androidSdk, - nativeSdk = nativeSdk, - ) + SentryFlutter( + androidSdk = androidSdk, + nativeSdk = nativeSdk, + ) } override fun onMethodCall(call: MethodCall, result: Result) { @@ -124,13 +124,13 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { options.integrations.removeAll { it is ReplayIntegration } val recorder = SentryFlutterReplay.recorder if (recorder != null && options.cacheDirPath != null) { - val replay = ReplayIntegration( - context, - dateProvider = CurrentDateProvider.getInstance(), - recorderProvider = { recorder }, - recorderConfigProvider = null, // TODO implement in dart - replayCacheProvider = null - ) + val replay = + ReplayIntegration( + context, + dateProvider = CurrentDateProvider.getInstance(), + recorderProvider = { recorder }, + recorderConfigProvider = null, // TODO implement in dart + replayCacheProvider = null) options.addIntegration(replay) options.setReplayController(replay) @@ -151,7 +151,7 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { val appStartTime = AppStartMetrics.getInstance().appStartTimeSpan.startTimestamp val isColdStart = - AppStartMetrics.getInstance().appStartType == AppStartMetrics.AppStartType.COLD + AppStartMetrics.getInstance().appStartType == AppStartMetrics.AppStartType.COLD if (appStartTime == null) { Log.w("Sentry", "App start won't be sent due to missing appStartTime") @@ -159,9 +159,9 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } else { val appStartTimeMillis = DateUtils.nanosToMillis(appStartTime.nanoTimestamp().toDouble()) val item = mapOf( - "appStartTime" to appStartTimeMillis, - "isColdStart" to isColdStart, - ) + "appStartTime" to appStartTimeMillis, + "isColdStart" to isColdStart, + ) result.success(item) } } @@ -199,10 +199,10 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { result.success(null) } else { val frames = mapOf( - "totalFrames" to total, - "slowFrames" to slow, - "frozenFrames" to frozen, - ) + "totalFrames" to total, + "slowFrames" to slow, + "frozenFrames" to frozen, + ) result.success(frames) } } @@ -352,7 +352,7 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } private class BeforeSendCallbackImpl( - private val sdkVersion: SdkVersion?, + private val sdkVersion: SdkVersion?, ) : SentryOptions.BeforeSendCallback { override fun execute(event: SentryEvent, hint: Hint): SentryEvent { setEventOriginTag(event) @@ -366,6 +366,7 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { private const val flutterSdk = "sentry.dart.flutter" private const val androidSdk = "sentry.java.android.flutter" private const val nativeSdk = "sentry.native.android.flutter" + private fun setEventOriginTag(event: SentryEvent) { event.sdk?.let { when (it.name) { @@ -378,9 +379,9 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } private fun setEventEnvironmentTag( - event: SentryEvent, - origin: String = "android", - environment: String, + event: SentryEvent, + origin: String = "android", + environment: String, ) { event.setTag("event.origin", origin) event.setTag("event.environment", environment) @@ -408,11 +409,11 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } val currentScope = InternalSentrySdk.getCurrentScope() val serializedScope = - InternalSentrySdk.serializeScope( - context, - options, - currentScope, - ) + InternalSentrySdk.serializeScope( + context, + options, + currentScope, + ) result.success(serializedScope) } } diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt index 4ee6fb2ede..c46386ffaa 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt @@ -1,7 +1,6 @@ package io.sentry.flutter import io.sentry.android.replay.Recorder -import io.sentry.android.replay.ScreenshotRecorderCallback import io.sentry.android.replay.ReplayIntegration object SentryFlutterReplay { From 658a132128e2aaec989c69daf046163043f99755 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 26 Apr 2024 13:58:49 +0200 Subject: [PATCH 21/74] android fixes --- flutter/android/proguard-rules.pro | 6 + .../src/main/cpp/sentry_android_binding.c | 2275 ++++++++-------- flutter/ffi-jni.yaml | 5 +- flutter/lib/src/native/java/binding.dart | 2400 +++++++++-------- .../src/native/java/sentry_native_java.dart | 3 + flutter/pubspec.yaml | 4 +- 6 files changed, 2376 insertions(+), 2317 deletions(-) diff --git a/flutter/android/proguard-rules.pro b/flutter/android/proguard-rules.pro index d64759b4c6..ff81e3db54 100644 --- a/flutter/android/proguard-rules.pro +++ b/flutter/android/proguard-rules.pro @@ -1,5 +1,11 @@ -keep class io.sentry.flutter.** { *; } +# JNI generated binding code (keep up to date with ffi-jni.yaml). +-keep class io.sentry.android.replay.Recorder +-keep class io.sentry.android.replay.ScreenshotRecorderConfig +-keep class io.sentry.android.replay.ReplayIntegration +-keep class java.io.File + # To ensure that stack traces is unambiguous # https://developer.android.com/studio/build/shrink-code#decode-stack-trace -keepattributes LineNumberTable,SourceFile diff --git a/flutter/android/src/main/cpp/sentry_android_binding.c b/flutter/android/src/main/cpp/sentry_android_binding.c index 9151710183..a2b17ab679 100644 --- a/flutter/android/src/main/cpp/sentry_android_binding.c +++ b/flutter/android/src/main/cpp/sentry_android_binding.c @@ -15,1797 +15,1820 @@ void setJniGetters(JniContext* (*cg)(void), JNIEnv* (*eg)(void)) { env_getter = eg; } -// io.sentry.android.replay.Recorder -jclass _c_Recorder = NULL; +// java.io.File +jclass _c_File = NULL; -jmethodID _m_Recorder__start = NULL; +jmethodID _m_File__new0 = NULL; FFI_PLUGIN_EXPORT -JniResult Recorder__start(jobject self_, jobject screenshotRecorderConfig) { +JniResult File__new0(jobject string) { load_env(); - load_class_global_ref(&_c_Recorder, "io/sentry/android/replay/Recorder"); - if (_c_Recorder == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_Recorder, &_m_Recorder__start, "start", - "(Lio/sentry/android/replay/ScreenshotRecorderConfig;)V"); - if (_m_Recorder__start == NULL) + load_method(_c_File, &_m_File__new0, "", "(Ljava/lang/String;)V"); + if (_m_File__new0 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Recorder__start, - screenshotRecorderConfig); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->NewObject(jniEnv, _c_File, _m_File__new0, string); + return to_global_ref_result(_result); } -jmethodID _m_Recorder__resume = NULL; +jmethodID _m_File__new1 = NULL; FFI_PLUGIN_EXPORT -JniResult Recorder__resume(jobject self_) { +JniResult File__new1(jobject string, jobject string1) { load_env(); - load_class_global_ref(&_c_Recorder, "io/sentry/android/replay/Recorder"); - if (_c_Recorder == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_Recorder, &_m_Recorder__resume, "resume", "()V"); - if (_m_Recorder__resume == NULL) + load_method(_c_File, &_m_File__new1, "", + "(Ljava/lang/String;Ljava/lang/String;)V"); + if (_m_File__new1 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Recorder__resume); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->NewObject(jniEnv, _c_File, _m_File__new1, string, string1); + return to_global_ref_result(_result); } -jmethodID _m_Recorder__pause = NULL; +jmethodID _m_File__new2 = NULL; FFI_PLUGIN_EXPORT -JniResult Recorder__pause(jobject self_) { +JniResult File__new2(jobject file, jobject string) { load_env(); - load_class_global_ref(&_c_Recorder, "io/sentry/android/replay/Recorder"); - if (_c_Recorder == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_Recorder, &_m_Recorder__pause, "pause", "()V"); - if (_m_Recorder__pause == NULL) + load_method(_c_File, &_m_File__new2, "", + "(Ljava/io/File;Ljava/lang/String;)V"); + if (_m_File__new2 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Recorder__pause); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->NewObject(jniEnv, _c_File, _m_File__new2, file, string); + return to_global_ref_result(_result); } -jmethodID _m_Recorder__stop = NULL; +jmethodID _m_File__new3 = NULL; FFI_PLUGIN_EXPORT -JniResult Recorder__stop(jobject self_) { +JniResult File__new3(jobject uRI) { load_env(); - load_class_global_ref(&_c_Recorder, "io/sentry/android/replay/Recorder"); - if (_c_Recorder == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_Recorder, &_m_Recorder__stop, "stop", "()V"); - if (_m_Recorder__stop == NULL) + load_method(_c_File, &_m_File__new3, "", "(Ljava/net/URI;)V"); + if (_m_File__new3 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Recorder__stop); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->NewObject(jniEnv, _c_File, _m_File__new3, uRI); + return to_global_ref_result(_result); } -// io.sentry.android.replay.ScreenshotRecorderConfig$Companion -jclass _c_ScreenshotRecorderConfig_Companion = NULL; - -jmethodID _m_ScreenshotRecorderConfig_Companion__from = NULL; +jmethodID _m_File__getName = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig_Companion__from( - jobject self_, - jobject context, - jobject sentryReplayOptions) { +JniResult File__getName(jobject self_) { load_env(); - load_class_global_ref( - &_c_ScreenshotRecorderConfig_Companion, - "io/sentry/android/replay/ScreenshotRecorderConfig$Companion"); - if (_c_ScreenshotRecorderConfig_Companion == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig_Companion, - &_m_ScreenshotRecorderConfig_Companion__from, "from", - "(Landroid/content/Context;Lio/sentry/SentryReplayOptions;)Lio/" - "sentry/android/replay/ScreenshotRecorderConfig;"); - if (_m_ScreenshotRecorderConfig_Companion__from == NULL) + load_method(_c_File, &_m_File__getName, "getName", "()Ljava/lang/String;"); + if (_m_File__getName == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig_Companion__from, context, - sentryReplayOptions); + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getName); return to_global_ref_result(_result); } -jmethodID _m_ScreenshotRecorderConfig_Companion__new0 = NULL; +jmethodID _m_File__getParent = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig_Companion__new0( - jobject defaultConstructorMarker) { +JniResult File__getParent(jobject self_) { load_env(); - load_class_global_ref( - &_c_ScreenshotRecorderConfig_Companion, - "io/sentry/android/replay/ScreenshotRecorderConfig$Companion"); - if (_c_ScreenshotRecorderConfig_Companion == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig_Companion, - &_m_ScreenshotRecorderConfig_Companion__new0, "", - "(Lkotlin/jvm/internal/DefaultConstructorMarker;)V"); - if (_m_ScreenshotRecorderConfig_Companion__new0 == NULL) + load_method(_c_File, &_m_File__getParent, "getParent", + "()Ljava/lang/String;"); + if (_m_File__getParent == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->NewObject( - jniEnv, _c_ScreenshotRecorderConfig_Companion, - _m_ScreenshotRecorderConfig_Companion__new0, defaultConstructorMarker); + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getParent); return to_global_ref_result(_result); } -// io.sentry.android.replay.ScreenshotRecorderConfig -jclass _c_ScreenshotRecorderConfig = NULL; - -jmethodID _m_ScreenshotRecorderConfig__new0 = NULL; +jmethodID _m_File__getParentFile = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__new0(int32_t i, - int32_t i1, - float f, - float f1, - int32_t i2, - int32_t i3) { +JniResult File__getParentFile(jobject self_) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, &_m_ScreenshotRecorderConfig__new0, - "", "(IIFFII)V"); - if (_m_ScreenshotRecorderConfig__new0 == NULL) + load_method(_c_File, &_m_File__getParentFile, "getParentFile", + "()Ljava/io/File;"); + if (_m_File__getParentFile == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->NewObject(jniEnv, _c_ScreenshotRecorderConfig, - _m_ScreenshotRecorderConfig__new0, i, - i1, f, f1, i2, i3); + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getParentFile); return to_global_ref_result(_result); } -jmethodID _m_ScreenshotRecorderConfig__getRecordingWidth = NULL; +jmethodID _m_File__getPath = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__getRecordingWidth(jobject self_) { +JniResult File__getPath(jobject self_) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__getRecordingWidth, - "getRecordingWidth", "()I"); - if (_m_ScreenshotRecorderConfig__getRecordingWidth == NULL) + load_method(_c_File, &_m_File__getPath, "getPath", "()Ljava/lang/String;"); + if (_m_File__getPath == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__getRecordingWidth); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getPath); + return to_global_ref_result(_result); } -jmethodID _m_ScreenshotRecorderConfig__getRecordingHeight = NULL; +jmethodID _m_File__isAbsolute = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__getRecordingHeight(jobject self_) { +JniResult File__isAbsolute(jobject self_) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__getRecordingHeight, - "getRecordingHeight", "()I"); - if (_m_ScreenshotRecorderConfig__getRecordingHeight == NULL) + load_method(_c_File, &_m_File__isAbsolute, "isAbsolute", "()Z"); + if (_m_File__isAbsolute == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__getRecordingHeight); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__isAbsolute); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_ScreenshotRecorderConfig__getScaleFactorX = NULL; +jmethodID _m_File__getAbsolutePath = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__getScaleFactorX(jobject self_) { +JniResult File__getAbsolutePath(jobject self_) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__getScaleFactorX, "getScaleFactorX", - "()F"); - if (_m_ScreenshotRecorderConfig__getScaleFactorX == NULL) + load_method(_c_File, &_m_File__getAbsolutePath, "getAbsolutePath", + "()Ljava/lang/String;"); + if (_m_File__getAbsolutePath == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - float _result = (*jniEnv)->CallFloatMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__getScaleFactorX); - return (JniResult){.value = {.f = _result}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getAbsolutePath); + return to_global_ref_result(_result); } -jmethodID _m_ScreenshotRecorderConfig__getScaleFactorY = NULL; +jmethodID _m_File__getAbsoluteFile = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__getScaleFactorY(jobject self_) { +JniResult File__getAbsoluteFile(jobject self_) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__getScaleFactorY, "getScaleFactorY", - "()F"); - if (_m_ScreenshotRecorderConfig__getScaleFactorY == NULL) + load_method(_c_File, &_m_File__getAbsoluteFile, "getAbsoluteFile", + "()Ljava/io/File;"); + if (_m_File__getAbsoluteFile == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - float _result = (*jniEnv)->CallFloatMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__getScaleFactorY); - return (JniResult){.value = {.f = _result}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getAbsoluteFile); + return to_global_ref_result(_result); } -jmethodID _m_ScreenshotRecorderConfig__getFrameRate = NULL; +jmethodID _m_File__getCanonicalPath = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__getFrameRate(jobject self_) { +JniResult File__getCanonicalPath(jobject self_) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__getFrameRate, "getFrameRate", - "()I"); - if (_m_ScreenshotRecorderConfig__getFrameRate == NULL) + load_method(_c_File, &_m_File__getCanonicalPath, "getCanonicalPath", + "()Ljava/lang/String;"); + if (_m_File__getCanonicalPath == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__getFrameRate); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getCanonicalPath); + return to_global_ref_result(_result); } -jmethodID _m_ScreenshotRecorderConfig__getBitRate = NULL; +jmethodID _m_File__getCanonicalFile = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__getBitRate(jobject self_) { +JniResult File__getCanonicalFile(jobject self_) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__getBitRate, "getBitRate", "()I"); - if (_m_ScreenshotRecorderConfig__getBitRate == NULL) + load_method(_c_File, &_m_File__getCanonicalFile, "getCanonicalFile", + "()Ljava/io/File;"); + if (_m_File__getCanonicalFile == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__getBitRate); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getCanonicalFile); + return to_global_ref_result(_result); } -jmethodID _m_ScreenshotRecorderConfig__component1 = NULL; +jmethodID _m_File__toURL = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__component1(jobject self_) { +JniResult File__toURL(jobject self_) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__component1, "component1", "()I"); - if (_m_ScreenshotRecorderConfig__component1 == NULL) + load_method(_c_File, &_m_File__toURL, "toURL", "()Ljava/net/URL;"); + if (_m_File__toURL == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__component1); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__toURL); + return to_global_ref_result(_result); } -jmethodID _m_ScreenshotRecorderConfig__component2 = NULL; +jmethodID _m_File__toURI = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__component2(jobject self_) { +JniResult File__toURI(jobject self_) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__component2, "component2", "()I"); - if (_m_ScreenshotRecorderConfig__component2 == NULL) + load_method(_c_File, &_m_File__toURI, "toURI", "()Ljava/net/URI;"); + if (_m_File__toURI == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__component2); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__toURI); + return to_global_ref_result(_result); } -jmethodID _m_ScreenshotRecorderConfig__component3 = NULL; +jmethodID _m_File__canRead = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__component3(jobject self_) { +JniResult File__canRead(jobject self_) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__component3, "component3", "()F"); - if (_m_ScreenshotRecorderConfig__component3 == NULL) + load_method(_c_File, &_m_File__canRead, "canRead", "()Z"); + if (_m_File__canRead == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - float _result = (*jniEnv)->CallFloatMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__component3); - return (JniResult){.value = {.f = _result}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__canRead); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_ScreenshotRecorderConfig__component4 = NULL; +jmethodID _m_File__canWrite = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__component4(jobject self_) { +JniResult File__canWrite(jobject self_) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__component4, "component4", "()F"); - if (_m_ScreenshotRecorderConfig__component4 == NULL) + load_method(_c_File, &_m_File__canWrite, "canWrite", "()Z"); + if (_m_File__canWrite == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - float _result = (*jniEnv)->CallFloatMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__component4); - return (JniResult){.value = {.f = _result}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__canWrite); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_ScreenshotRecorderConfig__component5 = NULL; +jmethodID _m_File__exists = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__component5(jobject self_) { +JniResult File__exists(jobject self_) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__component5, "component5", "()I"); - if (_m_ScreenshotRecorderConfig__component5 == NULL) + load_method(_c_File, &_m_File__exists, "exists", "()Z"); + if (_m_File__exists == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__component5); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__exists); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_ScreenshotRecorderConfig__component6 = NULL; +jmethodID _m_File__isDirectory = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__component6(jobject self_) { +JniResult File__isDirectory(jobject self_) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__component6, "component6", "()I"); - if (_m_ScreenshotRecorderConfig__component6 == NULL) + load_method(_c_File, &_m_File__isDirectory, "isDirectory", "()Z"); + if (_m_File__isDirectory == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__component6); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__isDirectory); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_ScreenshotRecorderConfig__copy = NULL; +jmethodID _m_File__isFile = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__copy(jobject self_, - int32_t i, - int32_t i1, - float f, - float f1, - int32_t i2, - int32_t i3) { +JniResult File__isFile(jobject self_) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, &_m_ScreenshotRecorderConfig__copy, - "copy", - "(IIFFII)Lio/sentry/android/replay/ScreenshotRecorderConfig;"); - if (_m_ScreenshotRecorderConfig__copy == NULL) + load_method(_c_File, &_m_File__isFile, "isFile", "()Z"); + if (_m_File__isFile == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__copy, i, i1, f, f1, i2, i3); - return to_global_ref_result(_result); + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__isFile); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_ScreenshotRecorderConfig__toString1 = NULL; +jmethodID _m_File__isHidden = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__toString1(jobject self_) { +JniResult File__isHidden(jobject self_) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__toString1, "toString", - "()Ljava/lang/String;"); - if (_m_ScreenshotRecorderConfig__toString1 == NULL) + load_method(_c_File, &_m_File__isHidden, "isHidden", "()Z"); + if (_m_File__isHidden == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__toString1); - return to_global_ref_result(_result); + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__isHidden); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_ScreenshotRecorderConfig__hashCode1 = NULL; +jmethodID _m_File__lastModified = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__hashCode1(jobject self_) { +JniResult File__lastModified(jobject self_) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__hashCode1, "hashCode", "()I"); - if (_m_ScreenshotRecorderConfig__hashCode1 == NULL) + load_method(_c_File, &_m_File__lastModified, "lastModified", "()J"); + if (_m_File__lastModified == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__hashCode1); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; + int64_t _result = + (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__lastModified); + return (JniResult){.value = {.j = _result}, .exception = check_exception()}; } -jmethodID _m_ScreenshotRecorderConfig__equals1 = NULL; +jmethodID _m_File__length = NULL; FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__equals1(jobject self_, jobject object) { +JniResult File__length(jobject self_) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__equals1, "equals", - "(Ljava/lang/Object;)Z"); - if (_m_ScreenshotRecorderConfig__equals1 == NULL) + load_method(_c_File, &_m_File__length, "length", "()J"); + if (_m_File__length == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = (*jniEnv)->CallBooleanMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__equals1, object); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + int64_t _result = (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__length); + return (JniResult){.value = {.j = _result}, .exception = check_exception()}; } -jfieldID _f_ScreenshotRecorderConfig__Companion = NULL; +jmethodID _m_File__createNewFile = NULL; FFI_PLUGIN_EXPORT -JniResult get_ScreenshotRecorderConfig__Companion() { +JniResult File__createNewFile(jobject self_) { load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field( - _c_ScreenshotRecorderConfig, &_f_ScreenshotRecorderConfig__Companion, - "Companion", - "Lio/sentry/android/replay/ScreenshotRecorderConfig$Companion;"); - jobject _result = - (*jniEnv)->GetStaticObjectField(jniEnv, _c_ScreenshotRecorderConfig, - _f_ScreenshotRecorderConfig__Companion); - return to_global_ref_result(_result); + load_method(_c_File, &_m_File__createNewFile, "createNewFile", "()Z"); + if (_m_File__createNewFile == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__createNewFile); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -// io.sentry.android.replay.ReplayIntegration -jclass _c_ReplayIntegration = NULL; - -jmethodID _m_ReplayIntegration__new0 = NULL; +jmethodID _m_File__delete = NULL; FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__new0(jobject context, - jobject iCurrentDateProvider, - jobject function0, - jobject function1, - jobject function11) { +JniResult File__delete(jobject self_) { load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__new0, "", - "(Landroid/content/Context;Lio/sentry/transport/" - "ICurrentDateProvider;Lkotlin/jvm/functions/Function0;Lkotlin/" - "jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V"); - if (_m_ReplayIntegration__new0 == NULL) + load_method(_c_File, &_m_File__delete, "delete", "()Z"); + if (_m_File__delete == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->NewObject( - jniEnv, _c_ReplayIntegration, _m_ReplayIntegration__new0, context, - iCurrentDateProvider, function0, function1, function11); - return to_global_ref_result(_result); + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__delete); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_ReplayIntegration__new1 = NULL; +jmethodID _m_File__deleteOnExit = NULL; FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__new1(jobject context, - jobject iCurrentDateProvider, - jobject function0, - jobject function1, - jobject function11, - int32_t i, - jobject defaultConstructorMarker) { +JniResult File__deleteOnExit(jobject self_) { load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__new1, "", - "(Landroid/content/Context;Lio/sentry/transport/" - "ICurrentDateProvider;Lkotlin/jvm/functions/Function0;Lkotlin/" - "jvm/functions/Function1;Lkotlin/jvm/functions/" - "Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V"); - if (_m_ReplayIntegration__new1 == NULL) + load_method(_c_File, &_m_File__deleteOnExit, "deleteOnExit", "()V"); + if (_m_File__deleteOnExit == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->NewObject( - jniEnv, _c_ReplayIntegration, _m_ReplayIntegration__new1, context, - iCurrentDateProvider, function0, function1, function11, i, - defaultConstructorMarker); - return to_global_ref_result(_result); + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_File__deleteOnExit); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jmethodID _m_ReplayIntegration__new2 = NULL; +jmethodID _m_File__list = NULL; FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__new2(jobject context, - jobject iCurrentDateProvider) { +JniResult File__list(jobject self_) { load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method( - _c_ReplayIntegration, &_m_ReplayIntegration__new2, "", - "(Landroid/content/Context;Lio/sentry/transport/ICurrentDateProvider;)V"); - if (_m_ReplayIntegration__new2 == NULL) + load_method(_c_File, &_m_File__list, "list", "()[Ljava/lang/String;"); + if (_m_File__list == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->NewObject(jniEnv, _c_ReplayIntegration, - _m_ReplayIntegration__new2, context, - iCurrentDateProvider); + jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__list); return to_global_ref_result(_result); } -jmethodID _m_ReplayIntegration__getReplayCacheDir = NULL; +jmethodID _m_File__list1 = NULL; FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__getReplayCacheDir(jobject self_) { +JniResult File__list1(jobject self_, jobject filenameFilter) { load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__getReplayCacheDir, - "getReplayCacheDir", "()Ljava/io/File;"); - if (_m_ReplayIntegration__getReplayCacheDir == NULL) + load_method(_c_File, &_m_File__list1, "list", + "(Ljava/io/FilenameFilter;)[Ljava/lang/String;"); + if (_m_File__list1 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_ReplayIntegration__getReplayCacheDir); + jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__list1, + filenameFilter); return to_global_ref_result(_result); } -jmethodID _m_ReplayIntegration__register = NULL; +jmethodID _m_File__listFiles = NULL; FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__register(jobject self_, - jobject iHub, - jobject sentryOptions) { +JniResult File__listFiles(jobject self_) { load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__register, "register", - "(Lio/sentry/IHub;Lio/sentry/SentryOptions;)V"); - if (_m_ReplayIntegration__register == NULL) + load_method(_c_File, &_m_File__listFiles, "listFiles", "()[Ljava/io/File;"); + if (_m_File__listFiles == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__register, iHub, - sentryOptions); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__listFiles); + return to_global_ref_result(_result); } -jmethodID _m_ReplayIntegration__isRecording = NULL; +jmethodID _m_File__listFiles1 = NULL; FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__isRecording(jobject self_) { +JniResult File__listFiles1(jobject self_, jobject filenameFilter) { load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__isRecording, - "isRecording", "()Z"); - if (_m_ReplayIntegration__isRecording == NULL) + load_method(_c_File, &_m_File__listFiles1, "listFiles", + "(Ljava/io/FilenameFilter;)[Ljava/io/File;"); + if (_m_File__listFiles1 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = (*jniEnv)->CallBooleanMethod( - jniEnv, self_, _m_ReplayIntegration__isRecording); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_File__listFiles1, filenameFilter); + return to_global_ref_result(_result); } -jmethodID _m_ReplayIntegration__start = NULL; +jmethodID _m_File__listFiles2 = NULL; FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__start(jobject self_) { +JniResult File__listFiles2(jobject self_, jobject fileFilter) { load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__start, "start", - "()V"); - if (_m_ReplayIntegration__start == NULL) + load_method(_c_File, &_m_File__listFiles2, "listFiles", + "(Ljava/io/FileFilter;)[Ljava/io/File;"); + if (_m_File__listFiles2 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__start); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_File__listFiles2, fileFilter); + return to_global_ref_result(_result); } -jmethodID _m_ReplayIntegration__resume = NULL; +jmethodID _m_File__mkdir = NULL; FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__resume(jobject self_) { +JniResult File__mkdir(jobject self_) { load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__resume, "resume", - "()V"); - if (_m_ReplayIntegration__resume == NULL) + load_method(_c_File, &_m_File__mkdir, "mkdir", "()Z"); + if (_m_File__mkdir == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__resume); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__mkdir); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_ReplayIntegration__sendReplayForEvent = NULL; +jmethodID _m_File__mkdirs = NULL; FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__sendReplayForEvent(jobject self_, - jobject sentryEvent, - jobject hint) { +JniResult File__mkdirs(jobject self_) { load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__sendReplayForEvent, - "sendReplayForEvent", - "(Lio/sentry/SentryEvent;Lio/sentry/Hint;)V"); - if (_m_ReplayIntegration__sendReplayForEvent == NULL) + load_method(_c_File, &_m_File__mkdirs, "mkdirs", "()Z"); + if (_m_File__mkdirs == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, - _m_ReplayIntegration__sendReplayForEvent, - sentryEvent, hint); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__mkdirs); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_ReplayIntegration__getReplayId = NULL; +jmethodID _m_File__renameTo = NULL; FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__getReplayId(jobject self_) { +JniResult File__renameTo(jobject self_, jobject file) { load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__getReplayId, - "getReplayId", "()Lio/sentry/protocol/SentryId;"); - if (_m_ReplayIntegration__getReplayId == NULL) + load_method(_c_File, &_m_File__renameTo, "renameTo", "(Ljava/io/File;)Z"); + if (_m_File__renameTo == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_ReplayIntegration__getReplayId); - return to_global_ref_result(_result); + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__renameTo, file); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_ReplayIntegration__pause = NULL; +jmethodID _m_File__setLastModified = NULL; FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__pause(jobject self_) { +JniResult File__setLastModified(jobject self_, int64_t j) { load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__pause, "pause", - "()V"); - if (_m_ReplayIntegration__pause == NULL) + load_method(_c_File, &_m_File__setLastModified, "setLastModified", "(J)Z"); + if (_m_File__setLastModified == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__pause); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setLastModified, j); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_ReplayIntegration__stop = NULL; +jmethodID _m_File__setReadOnly = NULL; FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__stop(jobject self_) { +JniResult File__setReadOnly(jobject self_) { load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__stop, "stop", "()V"); - if (_m_ReplayIntegration__stop == NULL) + load_method(_c_File, &_m_File__setReadOnly, "setReadOnly", "()Z"); + if (_m_File__setReadOnly == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__stop); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setReadOnly); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_ReplayIntegration__onScreenshotRecorded = NULL; +jmethodID _m_File__setWritable = NULL; FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__onScreenshotRecorded(jobject self_, - jobject bitmap) { +JniResult File__setWritable(jobject self_, uint8_t z, uint8_t z1) { load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__onScreenshotRecorded, - "onScreenshotRecorded", "(Landroid/graphics/Bitmap;)V"); - if (_m_ReplayIntegration__onScreenshotRecorded == NULL) + load_method(_c_File, &_m_File__setWritable, "setWritable", "(ZZ)Z"); + if (_m_File__setWritable == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, - _m_ReplayIntegration__onScreenshotRecorded, bitmap); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setWritable, z, z1); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_ReplayIntegration__onScreenshotRecorded1 = NULL; +jmethodID _m_File__setWritable1 = NULL; FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__onScreenshotRecorded1(jobject self_, - jobject file, - int64_t j) { +JniResult File__setWritable1(jobject self_, uint8_t z) { load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, - &_m_ReplayIntegration__onScreenshotRecorded1, - "onScreenshotRecorded", "(Ljava/io/File;J)V"); - if (_m_ReplayIntegration__onScreenshotRecorded1 == NULL) + load_method(_c_File, &_m_File__setWritable1, "setWritable", "(Z)Z"); + if (_m_File__setWritable1 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod( - jniEnv, self_, _m_ReplayIntegration__onScreenshotRecorded1, file, j); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setWritable1, z); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_ReplayIntegration__close = NULL; +jmethodID _m_File__setReadable = NULL; FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__close(jobject self_) { +JniResult File__setReadable(jobject self_, uint8_t z, uint8_t z1) { load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__close, "close", - "()V"); - if (_m_ReplayIntegration__close == NULL) + load_method(_c_File, &_m_File__setReadable, "setReadable", "(ZZ)Z"); + if (_m_File__setReadable == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__close); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setReadable, z, z1); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_ReplayIntegration__onConfigurationChanged = NULL; +jmethodID _m_File__setReadable1 = NULL; FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__onConfigurationChanged(jobject self_, - jobject configuration) { +JniResult File__setReadable1(jobject self_, uint8_t z) { load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method( - _c_ReplayIntegration, &_m_ReplayIntegration__onConfigurationChanged, - "onConfigurationChanged", "(Landroid/content/res/Configuration;)V"); - if (_m_ReplayIntegration__onConfigurationChanged == NULL) + load_method(_c_File, &_m_File__setReadable1, "setReadable", "(Z)Z"); + if (_m_File__setReadable1 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, - _m_ReplayIntegration__onConfigurationChanged, - configuration); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setReadable1, z); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_ReplayIntegration__onLowMemory = NULL; +jmethodID _m_File__setExecutable = NULL; FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__onLowMemory(jobject self_) { +JniResult File__setExecutable(jobject self_, uint8_t z, uint8_t z1) { load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__onLowMemory, - "onLowMemory", "()V"); - if (_m_ReplayIntegration__onLowMemory == NULL) + load_method(_c_File, &_m_File__setExecutable, "setExecutable", "(ZZ)Z"); + if (_m_File__setExecutable == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__onLowMemory); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = (*jniEnv)->CallBooleanMethod(jniEnv, self_, + _m_File__setExecutable, z, z1); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -// io.sentry.flutter.SentryFlutterReplay -jclass _c_SentryFlutterReplay = NULL; - -jmethodID _m_SentryFlutterReplay__getRecorder = NULL; +jmethodID _m_File__setExecutable1 = NULL; FFI_PLUGIN_EXPORT -JniResult SentryFlutterReplay__getRecorder(jobject self_) { +JniResult File__setExecutable1(jobject self_, uint8_t z) { load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__getRecorder, - "getRecorder", "()Lio/sentry/android/replay/Recorder;"); - if (_m_SentryFlutterReplay__getRecorder == NULL) + load_method(_c_File, &_m_File__setExecutable1, "setExecutable", "(Z)Z"); + if (_m_File__setExecutable1 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_SentryFlutterReplay__getRecorder); - return to_global_ref_result(_result); + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setExecutable1, z); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_SentryFlutterReplay__setRecorder = NULL; +jmethodID _m_File__canExecute = NULL; FFI_PLUGIN_EXPORT -JniResult SentryFlutterReplay__setRecorder(jobject self_, jobject recorder) { +JniResult File__canExecute(jobject self_) { load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__setRecorder, - "setRecorder", "(Lio/sentry/android/replay/Recorder;)V"); - if (_m_SentryFlutterReplay__setRecorder == NULL) + load_method(_c_File, &_m_File__canExecute, "canExecute", "()Z"); + if (_m_File__canExecute == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_SentryFlutterReplay__setRecorder, - recorder); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__canExecute); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_SentryFlutterReplay__getCacheDir = NULL; +jmethodID _m_File__listRoots = NULL; FFI_PLUGIN_EXPORT -JniResult SentryFlutterReplay__getCacheDir(jobject self_) { +JniResult File__listRoots() { load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__getCacheDir, - "getCacheDir", "()Ljava/lang/String;"); - if (_m_SentryFlutterReplay__getCacheDir == NULL) + load_static_method(_c_File, &_m_File__listRoots, "listRoots", + "()[Ljava/io/File;"); + if (_m_File__listRoots == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_SentryFlutterReplay__getCacheDir); + jobject _result = + (*jniEnv)->CallStaticObjectMethod(jniEnv, _c_File, _m_File__listRoots); return to_global_ref_result(_result); } -jmethodID _m_SentryFlutterReplay__getCallbackObject = NULL; +jmethodID _m_File__getTotalSpace = NULL; FFI_PLUGIN_EXPORT -JniResult SentryFlutterReplay__getCallbackObject(jobject self_) { +JniResult File__getTotalSpace(jobject self_) { load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_SentryFlutterReplay, - &_m_SentryFlutterReplay__getCallbackObject, "getCallbackObject", - "()Lio/sentry/android/replay/ScreenshotRecorderCallback;"); - if (_m_SentryFlutterReplay__getCallbackObject == NULL) + load_method(_c_File, &_m_File__getTotalSpace, "getTotalSpace", "()J"); + if (_m_File__getTotalSpace == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_SentryFlutterReplay__getCallbackObject); - return to_global_ref_result(_result); + int64_t _result = + (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__getTotalSpace); + return (JniResult){.value = {.j = _result}, .exception = check_exception()}; } -jfieldID _f_SentryFlutterReplay__INSTANCE = NULL; +jmethodID _m_File__getFreeSpace = NULL; FFI_PLUGIN_EXPORT -JniResult get_SentryFlutterReplay__INSTANCE() { +JniResult File__getFreeSpace(jobject self_) { load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__INSTANCE, - "INSTANCE", "Lio/sentry/flutter/SentryFlutterReplay;"); - jobject _result = (*jniEnv)->GetStaticObjectField( - jniEnv, _c_SentryFlutterReplay, _f_SentryFlutterReplay__INSTANCE); - return to_global_ref_result(_result); -} - -jfieldID _f_SentryFlutterReplay__recorder = NULL; -FFI_PLUGIN_EXPORT -JniResult get_SentryFlutterReplay__recorder() { - load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) + load_method(_c_File, &_m_File__getFreeSpace, "getFreeSpace", "()J"); + if (_m_File__getFreeSpace == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__recorder, - "recorder", "Lio/sentry/android/replay/Recorder;"); - jobject _result = (*jniEnv)->GetStaticObjectField( - jniEnv, _c_SentryFlutterReplay, _f_SentryFlutterReplay__recorder); - return to_global_ref_result(_result); + int64_t _result = + (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__getFreeSpace); + return (JniResult){.value = {.j = _result}, .exception = check_exception()}; } +jmethodID _m_File__getUsableSpace = NULL; FFI_PLUGIN_EXPORT -JniResult set_SentryFlutterReplay__recorder(jobject value) { +JniResult File__getUsableSpace(jobject self_) { load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__recorder, - "recorder", "Lio/sentry/android/replay/Recorder;"); - (*jniEnv)->SetStaticObjectField(jniEnv, _c_SentryFlutterReplay, - _f_SentryFlutterReplay__recorder, value); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_File, &_m_File__getUsableSpace, "getUsableSpace", "()J"); + if (_m_File__getUsableSpace == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + int64_t _result = + (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__getUsableSpace); + return (JniResult){.value = {.j = _result}, .exception = check_exception()}; } -jfieldID _f_SentryFlutterReplay__integration = NULL; +jmethodID _m_File__createTempFile = NULL; FFI_PLUGIN_EXPORT -JniResult get_SentryFlutterReplay__integration() { +JniResult File__createTempFile(jobject string, jobject string1, jobject file) { load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) + load_class_global_ref(&_c_File, "java/io/File"); + if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_SentryFlutterReplay, - &_f_SentryFlutterReplay__integration, "integration", - "Lio/sentry/android/replay/ReplayIntegration;"); - jobject _result = (*jniEnv)->GetStaticObjectField( - jniEnv, _c_SentryFlutterReplay, _f_SentryFlutterReplay__integration); + load_static_method( + _c_File, &_m_File__createTempFile, "createTempFile", + "(Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)Ljava/io/File;"); + if (_m_File__createTempFile == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallStaticObjectMethod( + jniEnv, _c_File, _m_File__createTempFile, string, string1, file); return to_global_ref_result(_result); } +jmethodID _m_File__createTempFile1 = NULL; FFI_PLUGIN_EXPORT -JniResult set_SentryFlutterReplay__integration(jobject value) { - load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_SentryFlutterReplay, - &_f_SentryFlutterReplay__integration, "integration", - "Lio/sentry/android/replay/ReplayIntegration;"); - (*jniEnv)->SetStaticObjectField(jniEnv, _c_SentryFlutterReplay, - _f_SentryFlutterReplay__integration, value); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -// java.io.File -jclass _c_File = NULL; - -jmethodID _m_File__new0 = NULL; -FFI_PLUGIN_EXPORT -JniResult File__new0(jobject string) { +JniResult File__createTempFile1(jobject string, jobject string1) { load_env(); load_class_global_ref(&_c_File, "java/io/File"); if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__new0, "", "(Ljava/lang/String;)V"); - if (_m_File__new0 == NULL) + load_static_method(_c_File, &_m_File__createTempFile1, "createTempFile", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/io/File;"); + if (_m_File__createTempFile1 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->NewObject(jniEnv, _c_File, _m_File__new0, string); + jobject _result = (*jniEnv)->CallStaticObjectMethod( + jniEnv, _c_File, _m_File__createTempFile1, string, string1); return to_global_ref_result(_result); } -jmethodID _m_File__new1 = NULL; +jmethodID _m_File__compareTo = NULL; FFI_PLUGIN_EXPORT -JniResult File__new1(jobject string, jobject string1) { +JniResult File__compareTo(jobject self_, jobject file) { load_env(); load_class_global_ref(&_c_File, "java/io/File"); if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__new1, "", - "(Ljava/lang/String;Ljava/lang/String;)V"); - if (_m_File__new1 == NULL) + load_method(_c_File, &_m_File__compareTo, "compareTo", "(Ljava/io/File;)I"); + if (_m_File__compareTo == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->NewObject(jniEnv, _c_File, _m_File__new1, string, string1); - return to_global_ref_result(_result); + int32_t _result = + (*jniEnv)->CallIntMethod(jniEnv, self_, _m_File__compareTo, file); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; } -jmethodID _m_File__new2 = NULL; +jmethodID _m_File__equals1 = NULL; FFI_PLUGIN_EXPORT -JniResult File__new2(jobject file, jobject string) { +JniResult File__equals1(jobject self_, jobject object) { load_env(); load_class_global_ref(&_c_File, "java/io/File"); if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__new2, "", - "(Ljava/io/File;Ljava/lang/String;)V"); - if (_m_File__new2 == NULL) + load_method(_c_File, &_m_File__equals1, "equals", "(Ljava/lang/Object;)Z"); + if (_m_File__equals1 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->NewObject(jniEnv, _c_File, _m_File__new2, file, string); - return to_global_ref_result(_result); + uint8_t _result = + (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__equals1, object); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_File__new3 = NULL; +jmethodID _m_File__hashCode1 = NULL; FFI_PLUGIN_EXPORT -JniResult File__new3(jobject uRI) { +JniResult File__hashCode1(jobject self_) { load_env(); load_class_global_ref(&_c_File, "java/io/File"); if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__new3, "", "(Ljava/net/URI;)V"); - if (_m_File__new3 == NULL) + load_method(_c_File, &_m_File__hashCode1, "hashCode", "()I"); + if (_m_File__hashCode1 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->NewObject(jniEnv, _c_File, _m_File__new3, uRI); - return to_global_ref_result(_result); + int32_t _result = (*jniEnv)->CallIntMethod(jniEnv, self_, _m_File__hashCode1); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; } -jmethodID _m_File__getName = NULL; +jmethodID _m_File__toString1 = NULL; FFI_PLUGIN_EXPORT -JniResult File__getName(jobject self_) { +JniResult File__toString1(jobject self_) { load_env(); load_class_global_ref(&_c_File, "java/io/File"); if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getName, "getName", "()Ljava/lang/String;"); - if (_m_File__getName == NULL) + load_method(_c_File, &_m_File__toString1, "toString", "()Ljava/lang/String;"); + if (_m_File__toString1 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getName); + (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__toString1); return to_global_ref_result(_result); } -jmethodID _m_File__getParent = NULL; +jmethodID _m_File__toPath = NULL; FFI_PLUGIN_EXPORT -JniResult File__getParent(jobject self_) { +JniResult File__toPath(jobject self_) { load_env(); load_class_global_ref(&_c_File, "java/io/File"); if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getParent, "getParent", - "()Ljava/lang/String;"); - if (_m_File__getParent == NULL) + load_method(_c_File, &_m_File__toPath, "toPath", "()Ljava/nio/file/Path;"); + if (_m_File__toPath == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getParent); + jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__toPath); return to_global_ref_result(_result); } -jmethodID _m_File__getParentFile = NULL; +jmethodID _m_File__compareTo1 = NULL; FFI_PLUGIN_EXPORT -JniResult File__getParentFile(jobject self_) { +JniResult File__compareTo1(jobject self_, jobject object) { load_env(); load_class_global_ref(&_c_File, "java/io/File"); if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getParentFile, "getParentFile", - "()Ljava/io/File;"); - if (_m_File__getParentFile == NULL) + load_method(_c_File, &_m_File__compareTo1, "compareTo", + "(Ljava/lang/Object;)I"); + if (_m_File__compareTo1 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getParentFile); - return to_global_ref_result(_result); + int32_t _result = + (*jniEnv)->CallIntMethod(jniEnv, self_, _m_File__compareTo1, object); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; } -jmethodID _m_File__getPath = NULL; +jfieldID _f_File__pathSeparator = NULL; FFI_PLUGIN_EXPORT -JniResult File__getPath(jobject self_) { +JniResult get_File__pathSeparator() { load_env(); load_class_global_ref(&_c_File, "java/io/File"); if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getPath, "getPath", "()Ljava/lang/String;"); - if (_m_File__getPath == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_field(_c_File, &_f_File__pathSeparator, "pathSeparator", + "Ljava/lang/String;"); jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getPath); + (*jniEnv)->GetStaticObjectField(jniEnv, _c_File, _f_File__pathSeparator); return to_global_ref_result(_result); } -jmethodID _m_File__isAbsolute = NULL; +jfieldID _f_File__pathSeparatorChar = NULL; FFI_PLUGIN_EXPORT -JniResult File__isAbsolute(jobject self_) { +JniResult get_File__pathSeparatorChar() { load_env(); load_class_global_ref(&_c_File, "java/io/File"); if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__isAbsolute, "isAbsolute", "()Z"); - if (_m_File__isAbsolute == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__isAbsolute); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + load_static_field(_c_File, &_f_File__pathSeparatorChar, "pathSeparatorChar", + "C"); + uint16_t _result = (*jniEnv)->GetStaticCharField(jniEnv, _c_File, + _f_File__pathSeparatorChar); + return (JniResult){.value = {.c = _result}, .exception = check_exception()}; } -jmethodID _m_File__getAbsolutePath = NULL; +jfieldID _f_File__separator = NULL; FFI_PLUGIN_EXPORT -JniResult File__getAbsolutePath(jobject self_) { +JniResult get_File__separator() { load_env(); load_class_global_ref(&_c_File, "java/io/File"); if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getAbsolutePath, "getAbsolutePath", - "()Ljava/lang/String;"); - if (_m_File__getAbsolutePath == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_field(_c_File, &_f_File__separator, "separator", + "Ljava/lang/String;"); jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getAbsolutePath); + (*jniEnv)->GetStaticObjectField(jniEnv, _c_File, _f_File__separator); return to_global_ref_result(_result); } -jmethodID _m_File__getAbsoluteFile = NULL; +jfieldID _f_File__separatorChar = NULL; FFI_PLUGIN_EXPORT -JniResult File__getAbsoluteFile(jobject self_) { +JniResult get_File__separatorChar() { load_env(); load_class_global_ref(&_c_File, "java/io/File"); if (_c_File == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getAbsoluteFile, "getAbsoluteFile", - "()Ljava/io/File;"); - if (_m_File__getAbsoluteFile == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getAbsoluteFile); - return to_global_ref_result(_result); + load_static_field(_c_File, &_f_File__separatorChar, "separatorChar", "C"); + uint16_t _result = + (*jniEnv)->GetStaticCharField(jniEnv, _c_File, _f_File__separatorChar); + return (JniResult){.value = {.c = _result}, .exception = check_exception()}; } -jmethodID _m_File__getCanonicalPath = NULL; +// io.sentry.flutter.SentryFlutterReplay +jclass _c_SentryFlutterReplay = NULL; + +jmethodID _m_SentryFlutterReplay__getRecorder = NULL; FFI_PLUGIN_EXPORT -JniResult File__getCanonicalPath(jobject self_) { +JniResult SentryFlutterReplay__getRecorder(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getCanonicalPath, "getCanonicalPath", - "()Ljava/lang/String;"); - if (_m_File__getCanonicalPath == NULL) + load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__getRecorder, + "getRecorder", "()Lio/sentry/android/replay/Recorder;"); + if (_m_SentryFlutterReplay__getRecorder == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getCanonicalPath); + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_SentryFlutterReplay__getRecorder); return to_global_ref_result(_result); } -jmethodID _m_File__getCanonicalFile = NULL; +jmethodID _m_SentryFlutterReplay__setRecorder = NULL; FFI_PLUGIN_EXPORT -JniResult File__getCanonicalFile(jobject self_) { +JniResult SentryFlutterReplay__setRecorder(jobject self_, jobject recorder) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getCanonicalFile, "getCanonicalFile", - "()Ljava/io/File;"); - if (_m_File__getCanonicalFile == NULL) + load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__setRecorder, + "setRecorder", "(Lio/sentry/android/replay/Recorder;)V"); + if (_m_SentryFlutterReplay__setRecorder == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getCanonicalFile); - return to_global_ref_result(_result); + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_SentryFlutterReplay__setRecorder, + recorder); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jmethodID _m_File__toURL = NULL; +jmethodID _m_SentryFlutterReplay__getIntegration = NULL; FFI_PLUGIN_EXPORT -JniResult File__toURL(jobject self_) { +JniResult SentryFlutterReplay__getIntegration(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__toURL, "toURL", "()Ljava/net/URL;"); - if (_m_File__toURL == NULL) + load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__getIntegration, + "getIntegration", + "()Lio/sentry/android/replay/ReplayIntegration;"); + if (_m_SentryFlutterReplay__getIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__toURL); + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_SentryFlutterReplay__getIntegration); return to_global_ref_result(_result); } -jmethodID _m_File__toURI = NULL; +jmethodID _m_SentryFlutterReplay__setIntegration = NULL; FFI_PLUGIN_EXPORT -JniResult File__toURI(jobject self_) { +JniResult SentryFlutterReplay__setIntegration(jobject self_, + jobject replayIntegration) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__toURI, "toURI", "()Ljava/net/URI;"); - if (_m_File__toURI == NULL) + load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__setIntegration, + "setIntegration", + "(Lio/sentry/android/replay/ReplayIntegration;)V"); + if (_m_SentryFlutterReplay__setIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__toURI); - return to_global_ref_result(_result); + (*jniEnv)->CallVoidMethod( + jniEnv, self_, _m_SentryFlutterReplay__setIntegration, replayIntegration); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jmethodID _m_File__canRead = NULL; +jfieldID _f_SentryFlutterReplay__INSTANCE = NULL; FFI_PLUGIN_EXPORT -JniResult File__canRead(jobject self_) { +JniResult get_SentryFlutterReplay__INSTANCE() { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__canRead, "canRead", "()Z"); - if (_m_File__canRead == NULL) + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__canRead); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__INSTANCE, + "INSTANCE", "Lio/sentry/flutter/SentryFlutterReplay;"); + jobject _result = (*jniEnv)->GetStaticObjectField( + jniEnv, _c_SentryFlutterReplay, _f_SentryFlutterReplay__INSTANCE); + return to_global_ref_result(_result); } -jmethodID _m_File__canWrite = NULL; +jfieldID _f_SentryFlutterReplay__recorder = NULL; FFI_PLUGIN_EXPORT -JniResult File__canWrite(jobject self_) { +JniResult get_SentryFlutterReplay__recorder() { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__canWrite, "canWrite", "()Z"); - if (_m_File__canWrite == NULL) + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__canWrite); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__recorder, + "recorder", "Lio/sentry/android/replay/Recorder;"); + jobject _result = (*jniEnv)->GetStaticObjectField( + jniEnv, _c_SentryFlutterReplay, _f_SentryFlutterReplay__recorder); + return to_global_ref_result(_result); } -jmethodID _m_File__exists = NULL; FFI_PLUGIN_EXPORT -JniResult File__exists(jobject self_) { +JniResult set_SentryFlutterReplay__recorder(jobject value) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__exists, "exists", "()Z"); - if (_m_File__exists == NULL) + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__exists); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__recorder, + "recorder", "Lio/sentry/android/replay/Recorder;"); + (*jniEnv)->SetStaticObjectField(jniEnv, _c_SentryFlutterReplay, + _f_SentryFlutterReplay__recorder, value); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jmethodID _m_File__isDirectory = NULL; +jfieldID _f_SentryFlutterReplay__integration = NULL; FFI_PLUGIN_EXPORT -JniResult File__isDirectory(jobject self_) { +JniResult get_SentryFlutterReplay__integration() { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__isDirectory, "isDirectory", "()Z"); - if (_m_File__isDirectory == NULL) + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__isDirectory); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + load_static_field(_c_SentryFlutterReplay, + &_f_SentryFlutterReplay__integration, "integration", + "Lio/sentry/android/replay/ReplayIntegration;"); + jobject _result = (*jniEnv)->GetStaticObjectField( + jniEnv, _c_SentryFlutterReplay, _f_SentryFlutterReplay__integration); + return to_global_ref_result(_result); } -jmethodID _m_File__isFile = NULL; FFI_PLUGIN_EXPORT -JniResult File__isFile(jobject self_) { +JniResult set_SentryFlutterReplay__integration(jobject value) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__isFile, "isFile", "()Z"); - if (_m_File__isFile == NULL) + load_class_global_ref(&_c_SentryFlutterReplay, + "io/sentry/flutter/SentryFlutterReplay"); + if (_c_SentryFlutterReplay == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__isFile); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + load_static_field(_c_SentryFlutterReplay, + &_f_SentryFlutterReplay__integration, "integration", + "Lio/sentry/android/replay/ReplayIntegration;"); + (*jniEnv)->SetStaticObjectField(jniEnv, _c_SentryFlutterReplay, + _f_SentryFlutterReplay__integration, value); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jmethodID _m_File__isHidden = NULL; +// io.sentry.android.replay.Recorder +jclass _c_Recorder = NULL; + +jmethodID _m_Recorder__start = NULL; FFI_PLUGIN_EXPORT -JniResult File__isHidden(jobject self_) { +JniResult Recorder__start(jobject self_, jobject screenshotRecorderConfig) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_Recorder, "io/sentry/android/replay/Recorder"); + if (_c_Recorder == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__isHidden, "isHidden", "()Z"); - if (_m_File__isHidden == NULL) + load_method(_c_Recorder, &_m_Recorder__start, "start", + "(Lio/sentry/android/replay/ScreenshotRecorderConfig;)V"); + if (_m_Recorder__start == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__isHidden); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Recorder__start, + screenshotRecorderConfig); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jmethodID _m_File__lastModified = NULL; +jmethodID _m_Recorder__resume = NULL; FFI_PLUGIN_EXPORT -JniResult File__lastModified(jobject self_) { +JniResult Recorder__resume(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_Recorder, "io/sentry/android/replay/Recorder"); + if (_c_Recorder == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__lastModified, "lastModified", "()J"); - if (_m_File__lastModified == NULL) + load_method(_c_Recorder, &_m_Recorder__resume, "resume", "()V"); + if (_m_Recorder__resume == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int64_t _result = - (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__lastModified); - return (JniResult){.value = {.j = _result}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Recorder__resume); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jmethodID _m_File__length = NULL; +jmethodID _m_Recorder__pause = NULL; FFI_PLUGIN_EXPORT -JniResult File__length(jobject self_) { +JniResult Recorder__pause(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_Recorder, "io/sentry/android/replay/Recorder"); + if (_c_Recorder == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__length, "length", "()J"); - if (_m_File__length == NULL) + load_method(_c_Recorder, &_m_Recorder__pause, "pause", "()V"); + if (_m_Recorder__pause == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int64_t _result = (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__length); - return (JniResult){.value = {.j = _result}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Recorder__pause); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jmethodID _m_File__createNewFile = NULL; +jmethodID _m_Recorder__stop = NULL; FFI_PLUGIN_EXPORT -JniResult File__createNewFile(jobject self_) { +JniResult Recorder__stop(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_Recorder, "io/sentry/android/replay/Recorder"); + if (_c_Recorder == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__createNewFile, "createNewFile", "()Z"); - if (_m_File__createNewFile == NULL) + load_method(_c_Recorder, &_m_Recorder__stop, "stop", "()V"); + if (_m_Recorder__stop == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__createNewFile); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Recorder__stop); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jmethodID _m_File__delete = NULL; +// io.sentry.android.replay.ScreenshotRecorderConfig$Companion +jclass _c_ScreenshotRecorderConfig_Companion = NULL; + +jmethodID _m_ScreenshotRecorderConfig_Companion__from = NULL; FFI_PLUGIN_EXPORT -JniResult File__delete(jobject self_) { +JniResult ScreenshotRecorderConfig_Companion__from( + jobject self_, + jobject context, + jobject sentryReplayOptions) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref( + &_c_ScreenshotRecorderConfig_Companion, + "io/sentry/android/replay/ScreenshotRecorderConfig$Companion"); + if (_c_ScreenshotRecorderConfig_Companion == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__delete, "delete", "()Z"); - if (_m_File__delete == NULL) + load_method(_c_ScreenshotRecorderConfig_Companion, + &_m_ScreenshotRecorderConfig_Companion__from, "from", + "(Landroid/content/Context;Lio/sentry/SentryReplayOptions;)Lio/" + "sentry/android/replay/ScreenshotRecorderConfig;"); + if (_m_ScreenshotRecorderConfig_Companion__from == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__delete); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig_Companion__from, context, + sentryReplayOptions); + return to_global_ref_result(_result); } -jmethodID _m_File__deleteOnExit = NULL; +jmethodID _m_ScreenshotRecorderConfig_Companion__new0 = NULL; FFI_PLUGIN_EXPORT -JniResult File__deleteOnExit(jobject self_) { +JniResult ScreenshotRecorderConfig_Companion__new0( + jobject defaultConstructorMarker) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref( + &_c_ScreenshotRecorderConfig_Companion, + "io/sentry/android/replay/ScreenshotRecorderConfig$Companion"); + if (_c_ScreenshotRecorderConfig_Companion == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__deleteOnExit, "deleteOnExit", "()V"); - if (_m_File__deleteOnExit == NULL) + load_method(_c_ScreenshotRecorderConfig_Companion, + &_m_ScreenshotRecorderConfig_Companion__new0, "", + "(Lkotlin/jvm/internal/DefaultConstructorMarker;)V"); + if (_m_ScreenshotRecorderConfig_Companion__new0 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_File__deleteOnExit); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->NewObject( + jniEnv, _c_ScreenshotRecorderConfig_Companion, + _m_ScreenshotRecorderConfig_Companion__new0, defaultConstructorMarker); + return to_global_ref_result(_result); } -jmethodID _m_File__list = NULL; +// io.sentry.android.replay.ScreenshotRecorderConfig +jclass _c_ScreenshotRecorderConfig = NULL; + +jmethodID _m_ScreenshotRecorderConfig__new0 = NULL; FFI_PLUGIN_EXPORT -JniResult File__list(jobject self_) { +JniResult ScreenshotRecorderConfig__new0(int32_t i, + int32_t i1, + float f, + float f1, + int32_t i2, + int32_t i3) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__list, "list", "()[Ljava/lang/String;"); - if (_m_File__list == NULL) + load_method(_c_ScreenshotRecorderConfig, &_m_ScreenshotRecorderConfig__new0, + "", "(IIFFII)V"); + if (_m_ScreenshotRecorderConfig__new0 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__list); + jobject _result = (*jniEnv)->NewObject(jniEnv, _c_ScreenshotRecorderConfig, + _m_ScreenshotRecorderConfig__new0, i, + i1, f, f1, i2, i3); return to_global_ref_result(_result); } -jmethodID _m_File__list1 = NULL; +jmethodID _m_ScreenshotRecorderConfig__getRecordingWidth = NULL; FFI_PLUGIN_EXPORT -JniResult File__list1(jobject self_, jobject filenameFilter) { +JniResult ScreenshotRecorderConfig__getRecordingWidth(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__list1, "list", - "(Ljava/io/FilenameFilter;)[Ljava/lang/String;"); - if (_m_File__list1 == NULL) + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__getRecordingWidth, + "getRecordingWidth", "()I"); + if (_m_ScreenshotRecorderConfig__getRecordingWidth == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__list1, - filenameFilter); - return to_global_ref_result(_result); + int32_t _result = (*jniEnv)->CallIntMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__getRecordingWidth); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; } -jmethodID _m_File__listFiles = NULL; +jmethodID _m_ScreenshotRecorderConfig__getRecordingHeight = NULL; FFI_PLUGIN_EXPORT -JniResult File__listFiles(jobject self_) { +JniResult ScreenshotRecorderConfig__getRecordingHeight(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__listFiles, "listFiles", "()[Ljava/io/File;"); - if (_m_File__listFiles == NULL) + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__getRecordingHeight, + "getRecordingHeight", "()I"); + if (_m_ScreenshotRecorderConfig__getRecordingHeight == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__listFiles); - return to_global_ref_result(_result); + int32_t _result = (*jniEnv)->CallIntMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__getRecordingHeight); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; } -jmethodID _m_File__listFiles1 = NULL; +jmethodID _m_ScreenshotRecorderConfig__getScaleFactorX = NULL; FFI_PLUGIN_EXPORT -JniResult File__listFiles1(jobject self_, jobject filenameFilter) { +JniResult ScreenshotRecorderConfig__getScaleFactorX(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__listFiles1, "listFiles", - "(Ljava/io/FilenameFilter;)[Ljava/io/File;"); - if (_m_File__listFiles1 == NULL) + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__getScaleFactorX, "getScaleFactorX", + "()F"); + if (_m_ScreenshotRecorderConfig__getScaleFactorX == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_File__listFiles1, filenameFilter); - return to_global_ref_result(_result); + float _result = (*jniEnv)->CallFloatMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__getScaleFactorX); + return (JniResult){.value = {.f = _result}, .exception = check_exception()}; } -jmethodID _m_File__listFiles2 = NULL; +jmethodID _m_ScreenshotRecorderConfig__getScaleFactorY = NULL; FFI_PLUGIN_EXPORT -JniResult File__listFiles2(jobject self_, jobject fileFilter) { +JniResult ScreenshotRecorderConfig__getScaleFactorY(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__listFiles2, "listFiles", - "(Ljava/io/FileFilter;)[Ljava/io/File;"); - if (_m_File__listFiles2 == NULL) + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__getScaleFactorY, "getScaleFactorY", + "()F"); + if (_m_ScreenshotRecorderConfig__getScaleFactorY == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_File__listFiles2, fileFilter); - return to_global_ref_result(_result); + float _result = (*jniEnv)->CallFloatMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__getScaleFactorY); + return (JniResult){.value = {.f = _result}, .exception = check_exception()}; } -jmethodID _m_File__mkdir = NULL; +jmethodID _m_ScreenshotRecorderConfig__getFrameRate = NULL; FFI_PLUGIN_EXPORT -JniResult File__mkdir(jobject self_) { +JniResult ScreenshotRecorderConfig__getFrameRate(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__mkdir, "mkdir", "()Z"); - if (_m_File__mkdir == NULL) + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__getFrameRate, "getFrameRate", + "()I"); + if (_m_ScreenshotRecorderConfig__getFrameRate == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__mkdir); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + int32_t _result = (*jniEnv)->CallIntMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__getFrameRate); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; } -jmethodID _m_File__mkdirs = NULL; +jmethodID _m_ScreenshotRecorderConfig__getBitRate = NULL; FFI_PLUGIN_EXPORT -JniResult File__mkdirs(jobject self_) { +JniResult ScreenshotRecorderConfig__getBitRate(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__mkdirs, "mkdirs", "()Z"); - if (_m_File__mkdirs == NULL) + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__getBitRate, "getBitRate", "()I"); + if (_m_ScreenshotRecorderConfig__getBitRate == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__mkdirs); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + int32_t _result = (*jniEnv)->CallIntMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__getBitRate); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; } -jmethodID _m_File__renameTo = NULL; +jmethodID _m_ScreenshotRecorderConfig__component1 = NULL; FFI_PLUGIN_EXPORT -JniResult File__renameTo(jobject self_, jobject file) { +JniResult ScreenshotRecorderConfig__component1(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__renameTo, "renameTo", "(Ljava/io/File;)Z"); - if (_m_File__renameTo == NULL) + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__component1, "component1", "()I"); + if (_m_ScreenshotRecorderConfig__component1 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__renameTo, file); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + int32_t _result = (*jniEnv)->CallIntMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__component1); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; } -jmethodID _m_File__setLastModified = NULL; +jmethodID _m_ScreenshotRecorderConfig__component2 = NULL; FFI_PLUGIN_EXPORT -JniResult File__setLastModified(jobject self_, int64_t j) { +JniResult ScreenshotRecorderConfig__component2(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__setLastModified, "setLastModified", "(J)Z"); - if (_m_File__setLastModified == NULL) + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__component2, "component2", "()I"); + if (_m_ScreenshotRecorderConfig__component2 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setLastModified, j); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + int32_t _result = (*jniEnv)->CallIntMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__component2); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; } -jmethodID _m_File__setReadOnly = NULL; +jmethodID _m_ScreenshotRecorderConfig__component3 = NULL; FFI_PLUGIN_EXPORT -JniResult File__setReadOnly(jobject self_) { +JniResult ScreenshotRecorderConfig__component3(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__setReadOnly, "setReadOnly", "()Z"); - if (_m_File__setReadOnly == NULL) + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__component3, "component3", "()F"); + if (_m_ScreenshotRecorderConfig__component3 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setReadOnly); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + float _result = (*jniEnv)->CallFloatMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__component3); + return (JniResult){.value = {.f = _result}, .exception = check_exception()}; } -jmethodID _m_File__setWritable = NULL; +jmethodID _m_ScreenshotRecorderConfig__component4 = NULL; FFI_PLUGIN_EXPORT -JniResult File__setWritable(jobject self_, uint8_t z, uint8_t z1) { +JniResult ScreenshotRecorderConfig__component4(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__setWritable, "setWritable", "(ZZ)Z"); - if (_m_File__setWritable == NULL) + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__component4, "component4", "()F"); + if (_m_ScreenshotRecorderConfig__component4 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setWritable, z, z1); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + float _result = (*jniEnv)->CallFloatMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__component4); + return (JniResult){.value = {.f = _result}, .exception = check_exception()}; } -jmethodID _m_File__setWritable1 = NULL; +jmethodID _m_ScreenshotRecorderConfig__component5 = NULL; FFI_PLUGIN_EXPORT -JniResult File__setWritable1(jobject self_, uint8_t z) { +JniResult ScreenshotRecorderConfig__component5(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__setWritable1, "setWritable", "(Z)Z"); - if (_m_File__setWritable1 == NULL) + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__component5, "component5", "()I"); + if (_m_ScreenshotRecorderConfig__component5 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setWritable1, z); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + int32_t _result = (*jniEnv)->CallIntMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__component5); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; } -jmethodID _m_File__setReadable = NULL; +jmethodID _m_ScreenshotRecorderConfig__component6 = NULL; FFI_PLUGIN_EXPORT -JniResult File__setReadable(jobject self_, uint8_t z, uint8_t z1) { +JniResult ScreenshotRecorderConfig__component6(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__setReadable, "setReadable", "(ZZ)Z"); - if (_m_File__setReadable == NULL) + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__component6, "component6", "()I"); + if (_m_ScreenshotRecorderConfig__component6 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setReadable, z, z1); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + int32_t _result = (*jniEnv)->CallIntMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__component6); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; } -jmethodID _m_File__setReadable1 = NULL; +jmethodID _m_ScreenshotRecorderConfig__copy = NULL; FFI_PLUGIN_EXPORT -JniResult File__setReadable1(jobject self_, uint8_t z) { +JniResult ScreenshotRecorderConfig__copy(jobject self_, + int32_t i, + int32_t i1, + float f, + float f1, + int32_t i2, + int32_t i3) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__setReadable1, "setReadable", "(Z)Z"); - if (_m_File__setReadable1 == NULL) + load_method(_c_ScreenshotRecorderConfig, &_m_ScreenshotRecorderConfig__copy, + "copy", + "(IIFFII)Lio/sentry/android/replay/ScreenshotRecorderConfig;"); + if (_m_ScreenshotRecorderConfig__copy == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setReadable1, z); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__copy, i, i1, f, f1, i2, i3); + return to_global_ref_result(_result); } -jmethodID _m_File__setExecutable = NULL; +jmethodID _m_ScreenshotRecorderConfig__toString1 = NULL; FFI_PLUGIN_EXPORT -JniResult File__setExecutable(jobject self_, uint8_t z, uint8_t z1) { +JniResult ScreenshotRecorderConfig__toString1(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__setExecutable, "setExecutable", "(ZZ)Z"); - if (_m_File__setExecutable == NULL) + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__toString1, "toString", + "()Ljava/lang/String;"); + if (_m_ScreenshotRecorderConfig__toString1 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = (*jniEnv)->CallBooleanMethod(jniEnv, self_, - _m_File__setExecutable, z, z1); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__toString1); + return to_global_ref_result(_result); } -jmethodID _m_File__setExecutable1 = NULL; +jmethodID _m_ScreenshotRecorderConfig__hashCode1 = NULL; FFI_PLUGIN_EXPORT -JniResult File__setExecutable1(jobject self_, uint8_t z) { +JniResult ScreenshotRecorderConfig__hashCode1(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__setExecutable1, "setExecutable", "(Z)Z"); - if (_m_File__setExecutable1 == NULL) + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__hashCode1, "hashCode", "()I"); + if (_m_ScreenshotRecorderConfig__hashCode1 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setExecutable1, z); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + int32_t _result = (*jniEnv)->CallIntMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__hashCode1); + return (JniResult){.value = {.i = _result}, .exception = check_exception()}; } -jmethodID _m_File__canExecute = NULL; +jmethodID _m_ScreenshotRecorderConfig__equals1 = NULL; FFI_PLUGIN_EXPORT -JniResult File__canExecute(jobject self_) { +JniResult ScreenshotRecorderConfig__equals1(jobject self_, jobject object) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__canExecute, "canExecute", "()Z"); - if (_m_File__canExecute == NULL) + load_method(_c_ScreenshotRecorderConfig, + &_m_ScreenshotRecorderConfig__equals1, "equals", + "(Ljava/lang/Object;)Z"); + if (_m_ScreenshotRecorderConfig__equals1 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__canExecute); + uint8_t _result = (*jniEnv)->CallBooleanMethod( + jniEnv, self_, _m_ScreenshotRecorderConfig__equals1, object); return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_File__listRoots = NULL; +jfieldID _f_ScreenshotRecorderConfig__Companion = NULL; FFI_PLUGIN_EXPORT -JniResult File__listRoots() { +JniResult get_ScreenshotRecorderConfig__Companion() { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_method(_c_File, &_m_File__listRoots, "listRoots", - "()[Ljava/io/File;"); - if (_m_File__listRoots == NULL) + load_class_global_ref(&_c_ScreenshotRecorderConfig, + "io/sentry/android/replay/ScreenshotRecorderConfig"); + if (_c_ScreenshotRecorderConfig == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_static_field( + _c_ScreenshotRecorderConfig, &_f_ScreenshotRecorderConfig__Companion, + "Companion", + "Lio/sentry/android/replay/ScreenshotRecorderConfig$Companion;"); jobject _result = - (*jniEnv)->CallStaticObjectMethod(jniEnv, _c_File, _m_File__listRoots); + (*jniEnv)->GetStaticObjectField(jniEnv, _c_ScreenshotRecorderConfig, + _f_ScreenshotRecorderConfig__Companion); return to_global_ref_result(_result); } -jmethodID _m_File__getTotalSpace = NULL; +// io.sentry.android.replay.ReplayIntegration +jclass _c_ReplayIntegration = NULL; + +jmethodID _m_ReplayIntegration__new0 = NULL; FFI_PLUGIN_EXPORT -JniResult File__getTotalSpace(jobject self_) { +JniResult ReplayIntegration__new0(jobject context, + jobject iCurrentDateProvider, + jobject function0, + jobject function1, + jobject function11) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getTotalSpace, "getTotalSpace", "()J"); - if (_m_File__getTotalSpace == NULL) + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__new0, "", + "(Landroid/content/Context;Lio/sentry/transport/" + "ICurrentDateProvider;Lkotlin/jvm/functions/Function0;Lkotlin/" + "jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V"); + if (_m_ReplayIntegration__new0 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int64_t _result = - (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__getTotalSpace); - return (JniResult){.value = {.j = _result}, .exception = check_exception()}; + jobject _result = (*jniEnv)->NewObject( + jniEnv, _c_ReplayIntegration, _m_ReplayIntegration__new0, context, + iCurrentDateProvider, function0, function1, function11); + return to_global_ref_result(_result); } -jmethodID _m_File__getFreeSpace = NULL; +jmethodID _m_ReplayIntegration__new1 = NULL; FFI_PLUGIN_EXPORT -JniResult File__getFreeSpace(jobject self_) { +JniResult ReplayIntegration__new1(jobject context, + jobject iCurrentDateProvider, + jobject function0, + jobject function1, + jobject function11, + int32_t i, + jobject defaultConstructorMarker) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getFreeSpace, "getFreeSpace", "()J"); - if (_m_File__getFreeSpace == NULL) + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__new1, "", + "(Landroid/content/Context;Lio/sentry/transport/" + "ICurrentDateProvider;Lkotlin/jvm/functions/Function0;Lkotlin/" + "jvm/functions/Function1;Lkotlin/jvm/functions/" + "Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V"); + if (_m_ReplayIntegration__new1 == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int64_t _result = - (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__getFreeSpace); - return (JniResult){.value = {.j = _result}, .exception = check_exception()}; + jobject _result = (*jniEnv)->NewObject( + jniEnv, _c_ReplayIntegration, _m_ReplayIntegration__new1, context, + iCurrentDateProvider, function0, function1, function11, i, + defaultConstructorMarker); + return to_global_ref_result(_result); } -jmethodID _m_File__getUsableSpace = NULL; +jmethodID _m_ReplayIntegration__new2 = NULL; FFI_PLUGIN_EXPORT -JniResult File__getUsableSpace(jobject self_) { +JniResult ReplayIntegration__new2(jobject context, + jobject iCurrentDateProvider) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method( + _c_ReplayIntegration, &_m_ReplayIntegration__new2, "", + "(Landroid/content/Context;Lio/sentry/transport/ICurrentDateProvider;)V"); + if (_m_ReplayIntegration__new2 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->NewObject(jniEnv, _c_ReplayIntegration, + _m_ReplayIntegration__new2, context, + iCurrentDateProvider); + return to_global_ref_result(_result); +} + +jmethodID _m_ReplayIntegration__getReplayCacheDir = NULL; +FFI_PLUGIN_EXPORT +JniResult ReplayIntegration__getReplayCacheDir(jobject self_) { + load_env(); + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__getReplayCacheDir, + "getReplayCacheDir", "()Ljava/io/File;"); + if (_m_ReplayIntegration__getReplayCacheDir == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_ReplayIntegration__getReplayCacheDir); + return to_global_ref_result(_result); +} + +jmethodID _m_ReplayIntegration__register = NULL; +FFI_PLUGIN_EXPORT +JniResult ReplayIntegration__register(jobject self_, + jobject iHub, + jobject sentryOptions) { + load_env(); + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__register, "register", + "(Lio/sentry/IHub;Lio/sentry/SentryOptions;)V"); + if (_m_ReplayIntegration__register == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__register, iHub, + sentryOptions); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; +} + +jmethodID _m_ReplayIntegration__isRecording = NULL; +FFI_PLUGIN_EXPORT +JniResult ReplayIntegration__isRecording(jobject self_) { + load_env(); + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getUsableSpace, "getUsableSpace", "()J"); - if (_m_File__getUsableSpace == NULL) + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__isRecording, + "isRecording", "()Z"); + if (_m_ReplayIntegration__isRecording == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int64_t _result = - (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__getUsableSpace); - return (JniResult){.value = {.j = _result}, .exception = check_exception()}; + uint8_t _result = (*jniEnv)->CallBooleanMethod( + jniEnv, self_, _m_ReplayIntegration__isRecording); + return (JniResult){.value = {.z = _result}, .exception = check_exception()}; } -jmethodID _m_File__createTempFile = NULL; +jmethodID _m_ReplayIntegration__start = NULL; FFI_PLUGIN_EXPORT -JniResult File__createTempFile(jobject string, jobject string1, jobject file) { +JniResult ReplayIntegration__start(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_method( - _c_File, &_m_File__createTempFile, "createTempFile", - "(Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)Ljava/io/File;"); - if (_m_File__createTempFile == NULL) + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__start, "start", + "()V"); + if (_m_ReplayIntegration__start == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallStaticObjectMethod( - jniEnv, _c_File, _m_File__createTempFile, string, string1, file); - return to_global_ref_result(_result); + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__start); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jmethodID _m_File__createTempFile1 = NULL; +jmethodID _m_ReplayIntegration__resume = NULL; FFI_PLUGIN_EXPORT -JniResult File__createTempFile1(jobject string, jobject string1) { +JniResult ReplayIntegration__resume(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_method(_c_File, &_m_File__createTempFile1, "createTempFile", - "(Ljava/lang/String;Ljava/lang/String;)Ljava/io/File;"); - if (_m_File__createTempFile1 == NULL) + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__resume, "resume", + "()V"); + if (_m_ReplayIntegration__resume == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallStaticObjectMethod( - jniEnv, _c_File, _m_File__createTempFile1, string, string1); - return to_global_ref_result(_result); + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__resume); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jmethodID _m_File__compareTo = NULL; +jmethodID _m_ReplayIntegration__sendReplayForEvent = NULL; FFI_PLUGIN_EXPORT -JniResult File__compareTo(jobject self_, jobject file) { +JniResult ReplayIntegration__sendReplayForEvent(jobject self_, + jobject sentryEvent, + jobject hint) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__compareTo, "compareTo", "(Ljava/io/File;)I"); - if (_m_File__compareTo == NULL) + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__sendReplayForEvent, + "sendReplayForEvent", + "(Lio/sentry/SentryEvent;Lio/sentry/Hint;)V"); + if (_m_ReplayIntegration__sendReplayForEvent == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = - (*jniEnv)->CallIntMethod(jniEnv, self_, _m_File__compareTo, file); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, + _m_ReplayIntegration__sendReplayForEvent, + sentryEvent, hint); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jmethodID _m_File__equals1 = NULL; +jmethodID _m_ReplayIntegration__sendReplay = NULL; FFI_PLUGIN_EXPORT -JniResult File__equals1(jobject self_, jobject object) { +JniResult ReplayIntegration__sendReplay(jobject self_, + jobject boolean, + jobject string, + jobject hint) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__equals1, "equals", "(Ljava/lang/Object;)Z"); - if (_m_File__equals1 == NULL) + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__sendReplay, + "sendReplay", + "(Ljava/lang/Boolean;Ljava/lang/String;Lio/sentry/Hint;)V"); + if (_m_ReplayIntegration__sendReplay == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__equals1, object); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__sendReplay, + boolean, string, hint); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jmethodID _m_File__hashCode1 = NULL; +jmethodID _m_ReplayIntegration__getReplayId = NULL; FFI_PLUGIN_EXPORT -JniResult File__hashCode1(jobject self_) { +JniResult ReplayIntegration__getReplayId(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__hashCode1, "hashCode", "()I"); - if (_m_File__hashCode1 == NULL) + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__getReplayId, + "getReplayId", "()Lio/sentry/protocol/SentryId;"); + if (_m_ReplayIntegration__getReplayId == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod(jniEnv, self_, _m_File__hashCode1); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; + jobject _result = (*jniEnv)->CallObjectMethod( + jniEnv, self_, _m_ReplayIntegration__getReplayId); + return to_global_ref_result(_result); } -jmethodID _m_File__toString1 = NULL; +jmethodID _m_ReplayIntegration__pause = NULL; FFI_PLUGIN_EXPORT -JniResult File__toString1(jobject self_) { +JniResult ReplayIntegration__pause(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__toString1, "toString", "()Ljava/lang/String;"); - if (_m_File__toString1 == NULL) + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__pause, "pause", + "()V"); + if (_m_ReplayIntegration__pause == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__toString1); - return to_global_ref_result(_result); + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__pause); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jmethodID _m_File__toPath = NULL; +jmethodID _m_ReplayIntegration__stop = NULL; FFI_PLUGIN_EXPORT -JniResult File__toPath(jobject self_) { +JniResult ReplayIntegration__stop(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__toPath, "toPath", "()Ljava/nio/file/Path;"); - if (_m_File__toPath == NULL) + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__stop, "stop", "()V"); + if (_m_ReplayIntegration__stop == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__toPath); - return to_global_ref_result(_result); + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__stop); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jmethodID _m_File__compareTo1 = NULL; +jmethodID _m_ReplayIntegration__onScreenshotRecorded = NULL; FFI_PLUGIN_EXPORT -JniResult File__compareTo1(jobject self_, jobject object) { +JniResult ReplayIntegration__onScreenshotRecorded(jobject self_, + jobject bitmap) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__compareTo1, "compareTo", - "(Ljava/lang/Object;)I"); - if (_m_File__compareTo1 == NULL) + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__onScreenshotRecorded, + "onScreenshotRecorded", "(Landroid/graphics/Bitmap;)V"); + if (_m_ReplayIntegration__onScreenshotRecorded == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = - (*jniEnv)->CallIntMethod(jniEnv, self_, _m_File__compareTo1, object); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, + _m_ReplayIntegration__onScreenshotRecorded, bitmap); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jfieldID _f_File__pathSeparator = NULL; +jmethodID _m_ReplayIntegration__onScreenshotRecorded1 = NULL; FFI_PLUGIN_EXPORT -JniResult get_File__pathSeparator() { +JniResult ReplayIntegration__onScreenshotRecorded1(jobject self_, + jobject file, + int64_t j) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_File, &_f_File__pathSeparator, "pathSeparator", - "Ljava/lang/String;"); - jobject _result = - (*jniEnv)->GetStaticObjectField(jniEnv, _c_File, _f_File__pathSeparator); - return to_global_ref_result(_result); + load_method(_c_ReplayIntegration, + &_m_ReplayIntegration__onScreenshotRecorded1, + "onScreenshotRecorded", "(Ljava/io/File;J)V"); + if (_m_ReplayIntegration__onScreenshotRecorded1 == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod( + jniEnv, self_, _m_ReplayIntegration__onScreenshotRecorded1, file, j); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jfieldID _f_File__pathSeparatorChar = NULL; +jmethodID _m_ReplayIntegration__close = NULL; FFI_PLUGIN_EXPORT -JniResult get_File__pathSeparatorChar() { +JniResult ReplayIntegration__close(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_File, &_f_File__pathSeparatorChar, "pathSeparatorChar", - "C"); - uint16_t _result = (*jniEnv)->GetStaticCharField(jniEnv, _c_File, - _f_File__pathSeparatorChar); - return (JniResult){.value = {.c = _result}, .exception = check_exception()}; + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__close, "close", + "()V"); + if (_m_ReplayIntegration__close == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__close); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jfieldID _f_File__separator = NULL; +jmethodID _m_ReplayIntegration__onConfigurationChanged = NULL; FFI_PLUGIN_EXPORT -JniResult get_File__separator() { +JniResult ReplayIntegration__onConfigurationChanged(jobject self_, + jobject configuration) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_File, &_f_File__separator, "separator", - "Ljava/lang/String;"); - jobject _result = - (*jniEnv)->GetStaticObjectField(jniEnv, _c_File, _f_File__separator); - return to_global_ref_result(_result); + load_method( + _c_ReplayIntegration, &_m_ReplayIntegration__onConfigurationChanged, + "onConfigurationChanged", "(Landroid/content/res/Configuration;)V"); + if (_m_ReplayIntegration__onConfigurationChanged == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, + _m_ReplayIntegration__onConfigurationChanged, + configuration); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } -jfieldID _f_File__separatorChar = NULL; +jmethodID _m_ReplayIntegration__onLowMemory = NULL; FFI_PLUGIN_EXPORT -JniResult get_File__separatorChar() { +JniResult ReplayIntegration__onLowMemory(jobject self_) { load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) + load_class_global_ref(&_c_ReplayIntegration, + "io/sentry/android/replay/ReplayIntegration"); + if (_c_ReplayIntegration == NULL) return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_File, &_f_File__separatorChar, "separatorChar", "C"); - uint16_t _result = - (*jniEnv)->GetStaticCharField(jniEnv, _c_File, _f_File__separatorChar); - return (JniResult){.value = {.c = _result}, .exception = check_exception()}; + load_method(_c_ReplayIntegration, &_m_ReplayIntegration__onLowMemory, + "onLowMemory", "()V"); + if (_m_ReplayIntegration__onLowMemory == NULL) + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; + (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__onLowMemory); + return (JniResult){.value = {.j = 0}, .exception = check_exception()}; } diff --git a/flutter/ffi-jni.yaml b/flutter/ffi-jni.yaml index e610fa69e7..6527558e0f 100644 --- a/flutter/ffi-jni.yaml +++ b/flutter/ffi-jni.yaml @@ -15,12 +15,13 @@ output: log_level: all +# Make sure to keep this list up to date with android/proguard-rules.pro classes: + - java.io.File + - io.sentry.flutter.SentryFlutterReplay - io.sentry.android.replay.Recorder - io.sentry.android.replay.ScreenshotRecorderConfig - io.sentry.android.replay.ReplayIntegration - - io.sentry.flutter.SentryFlutterReplay - - java.io.File enable_experiment: - interface_implementation diff --git a/flutter/lib/src/native/java/binding.dart b/flutter/lib/src/native/java/binding.dart index 19625de333..af656d57a2 100644 --- a/flutter/lib/src/native/java/binding.dart +++ b/flutter/lib/src/native/java/binding.dart @@ -25,867 +25,748 @@ import "package:jni/jni.dart" as jni; final ffi.Pointer Function(String sym) jniLookup = ProtectedJniExtensions.initGeneratedLibrary("sentry_android_binding"); -/// from: io.sentry.android.replay.Recorder -class Recorder extends jni.JObject { +/// from: java.io.File +class File extends jni.JObject { @override - late final jni.JObjType $type = type; + late final jni.JObjType $type = type; - Recorder.fromRef( + File.fromRef( jni.JObjectPtr ref, ) : super.fromRef(ref); /// The type which includes information such as the signature of this class. - static const type = $RecorderType(); - static final _start = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("Recorder__start") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); + static const type = $FileType(); + static final _get_pathSeparator = + jniLookup>( + "get_File__pathSeparator") + .asFunction(); - /// from: public abstract void start(io.sentry.android.replay.ScreenshotRecorderConfig screenshotRecorderConfig) - void start( - ScreenshotRecorderConfig screenshotRecorderConfig, - ) { - return _start(reference, screenshotRecorderConfig.reference).check(); - } + /// from: static public final java.lang.String pathSeparator + /// The returned object must be released after use, by calling the [release] method. + static jni.JString get pathSeparator => + const jni.JStringType().fromRef(_get_pathSeparator().object); - static final _resume = jniLookup< - ffi - .NativeFunction)>>( - "Recorder__resume") - .asFunction)>(); + static final _get_pathSeparatorChar = + jniLookup>( + "get_File__pathSeparatorChar") + .asFunction(); - /// from: public abstract void resume() - void resume() { - return _resume(reference).check(); - } + /// from: static public final char pathSeparatorChar + static int get pathSeparatorChar => _get_pathSeparatorChar().char; - static final _pause = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("Recorder__pause") - .asFunction)>(); + static final _get_separator = + jniLookup>( + "get_File__separator") + .asFunction(); - /// from: public abstract void pause() - void pause() { - return _pause(reference).check(); - } + /// from: static public final java.lang.String separator + /// The returned object must be released after use, by calling the [release] method. + static jni.JString get separator => + const jni.JStringType().fromRef(_get_separator().object); - static final _stop = jniLookup< + static final _get_separatorChar = + jniLookup>( + "get_File__separatorChar") + .asFunction(); + + /// from: static public final char separatorChar + static int get separatorChar => _get_separatorChar().char; + + static final _new0 = jniLookup< ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("Recorder__stop") + jni.JniResult Function(ffi.Pointer)>>("File__new0") .asFunction)>(); - /// from: public abstract void stop() - void stop() { - return _stop(reference).check(); + /// from: public void (java.lang.String string) + /// The returned object must be released after use, by calling the [release] method. + factory File( + jni.JString string, + ) { + return File.fromRef(_new0(string.reference).object); } - /// Maps a specific port to the implemented interface. - static final Map _$impls = {}; - ReceivePort? _$p; + static final _new1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>>("File__new1") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); - static jni.JObjectPtr _$invoke( - int port, - jni.JObjectPtr descriptor, - jni.JObjectPtr args, + /// from: public void (java.lang.String string, java.lang.String string1) + /// The returned object must be released after use, by calling the [release] method. + factory File.new1( + jni.JString string, + jni.JString string1, ) { - return _$invokeMethod( - port, - $MethodInvocation.fromAddresses( - 0, - descriptor.address, - args.address, - ), - ); + return File.fromRef(_new1(string.reference, string1.reference).object); } - static final ffi.Pointer< + static final _new2 = jniLookup< ffi.NativeFunction< - jni.JObjectPtr Function( - ffi.Uint64, jni.JObjectPtr, jni.JObjectPtr)>> - _$invokePointer = ffi.Pointer.fromFunction(_$invoke); + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>>("File__new2") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); - static ffi.Pointer _$invokeMethod( - int $p, - $MethodInvocation $i, + /// from: public void (java.io.File file, java.lang.String string) + /// The returned object must be released after use, by calling the [release] method. + factory File.new2( + File file, + jni.JString string, ) { - try { - final $d = $i.methodDescriptor.toDartString(releaseOriginal: true); - final $a = $i.args; - if ($d == - r"start(Lio/sentry/android/replay/ScreenshotRecorderConfig;)V") { - _$impls[$p]!.start( - $a[0].castTo(const $ScreenshotRecorderConfigType(), - releaseOriginal: true), - ); - return jni.nullptr; - } - if ($d == r"resume()V") { - _$impls[$p]!.resume(); - return jni.nullptr; - } - if ($d == r"pause()V") { - _$impls[$p]!.pause(); - return jni.nullptr; - } - if ($d == r"stop()V") { - _$impls[$p]!.stop(); - return jni.nullptr; - } - } catch (e) { - return ProtectedJniExtensions.newDartException(e.toString()); - } - return jni.nullptr; + return File.fromRef(_new2(file.reference, string.reference).object); } - factory Recorder.implement( - $RecorderImpl $impl, + static final _new3 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__new3") + .asFunction)>(); + + /// from: public void (java.net.URI uRI) + /// The returned object must be released after use, by calling the [release] method. + factory File.new3( + jni.JObject uRI, ) { - final $p = ReceivePort(); - final $x = Recorder.fromRef( - ProtectedJniExtensions.newPortProxy( - r"io.sentry.android.replay.Recorder", - $p, - _$invokePointer, - ), - ).._$p = $p; - final $a = $p.sendPort.nativePort; - _$impls[$a] = $impl; - $p.listen(($m) { - if ($m == null) { - _$impls.remove($p.sendPort.nativePort); - $p.close(); - return; - } - final $i = $MethodInvocation.fromMessage($m); - final $r = _$invokeMethod($p.sendPort.nativePort, $i); - ProtectedJniExtensions.returnResult($i.result, $r); - }); - return $x; + return File.fromRef(_new3(uRI.reference).object); } -} - -abstract class $RecorderImpl { - factory $RecorderImpl({ - required void Function(ScreenshotRecorderConfig screenshotRecorderConfig) - start, - required void Function() resume, - required void Function() pause, - required void Function() stop, - }) = _$RecorderImpl; - void start(ScreenshotRecorderConfig screenshotRecorderConfig); - void resume(); - void pause(); - void stop(); -} - -class _$RecorderImpl implements $RecorderImpl { - _$RecorderImpl({ - required void Function(ScreenshotRecorderConfig screenshotRecorderConfig) - start, - required void Function() resume, - required void Function() pause, - required void Function() stop, - }) : _start = start, - _resume = resume, - _pause = pause, - _stop = stop; - - final void Function(ScreenshotRecorderConfig screenshotRecorderConfig) _start; - final void Function() _resume; - final void Function() _pause; - final void Function() _stop; - - void start(ScreenshotRecorderConfig screenshotRecorderConfig) { - return _start(screenshotRecorderConfig); - } + static final _getName = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__getName") + .asFunction)>(); - void resume() { - return _resume(); + /// from: public java.lang.String getName() + /// The returned object must be released after use, by calling the [release] method. + jni.JString getName() { + return const jni.JStringType().fromRef(_getName(reference).object); } - void pause() { - return _pause(); - } + static final _getParent = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__getParent") + .asFunction)>(); - void stop() { - return _stop(); + /// from: public java.lang.String getParent() + /// The returned object must be released after use, by calling the [release] method. + jni.JString getParent() { + return const jni.JStringType().fromRef(_getParent(reference).object); } -} - -class $RecorderType extends jni.JObjType { - const $RecorderType(); - @override - String get signature => r"Lio/sentry/android/replay/Recorder;"; + static final _getParentFile = jniLookup< + ffi + .NativeFunction)>>( + "File__getParentFile") + .asFunction)>(); - @override - Recorder fromRef(jni.JObjectPtr ref) => Recorder.fromRef(ref); + /// from: public java.io.File getParentFile() + /// The returned object must be released after use, by calling the [release] method. + File getParentFile() { + return const $FileType().fromRef(_getParentFile(reference).object); + } - @override - jni.JObjType get superType => const jni.JObjectType(); + static final _getPath = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__getPath") + .asFunction)>(); - @override - final superCount = 1; + /// from: public java.lang.String getPath() + /// The returned object must be released after use, by calling the [release] method. + jni.JString getPath() { + return const jni.JStringType().fromRef(_getPath(reference).object); + } - @override - int get hashCode => ($RecorderType).hashCode; + static final _isAbsolute = jniLookup< + ffi + .NativeFunction)>>( + "File__isAbsolute") + .asFunction)>(); - @override - bool operator ==(Object other) { - return other.runtimeType == ($RecorderType) && other is $RecorderType; + /// from: public boolean isAbsolute() + bool isAbsolute() { + return _isAbsolute(reference).boolean; } -} -/// from: io.sentry.android.replay.ScreenshotRecorderConfig$Companion -class ScreenshotRecorderConfig_Companion extends jni.JObject { - @override - late final jni.JObjType $type = type; + static final _getAbsolutePath = jniLookup< + ffi + .NativeFunction)>>( + "File__getAbsolutePath") + .asFunction)>(); - ScreenshotRecorderConfig_Companion.fromRef( - jni.JObjectPtr ref, - ) : super.fromRef(ref); + /// from: public java.lang.String getAbsolutePath() + /// The returned object must be released after use, by calling the [release] method. + jni.JString getAbsolutePath() { + return const jni.JStringType().fromRef(_getAbsolutePath(reference).object); + } - /// The type which includes information such as the signature of this class. - static const type = $ScreenshotRecorderConfig_CompanionType(); - static final _from = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer, ffi.Pointer)>>( - "ScreenshotRecorderConfig_Companion__from") - .asFunction< - jni.JniResult Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer)>(); + static final _getAbsoluteFile = jniLookup< + ffi + .NativeFunction)>>( + "File__getAbsoluteFile") + .asFunction)>(); - /// from: public final io.sentry.android.replay.ScreenshotRecorderConfig from(android.content.Context context, io.sentry.SentryReplayOptions sentryReplayOptions) + /// from: public java.io.File getAbsoluteFile() /// The returned object must be released after use, by calling the [release] method. - ScreenshotRecorderConfig from( - jni.JObject context, - jni.JObject sentryReplayOptions, - ) { - return const $ScreenshotRecorderConfigType().fromRef( - _from(reference, context.reference, sentryReplayOptions.reference) - .object); + File getAbsoluteFile() { + return const $FileType().fromRef(_getAbsoluteFile(reference).object); } - static final _new0 = jniLookup< + static final _getCanonicalPath = jniLookup< ffi .NativeFunction)>>( - "ScreenshotRecorderConfig_Companion__new0") + "File__getCanonicalPath") .asFunction)>(); - /// from: public void (kotlin.jvm.internal.DefaultConstructorMarker defaultConstructorMarker) + /// from: public java.lang.String getCanonicalPath() /// The returned object must be released after use, by calling the [release] method. - factory ScreenshotRecorderConfig_Companion( - jni.JObject defaultConstructorMarker, - ) { - return ScreenshotRecorderConfig_Companion.fromRef( - _new0(defaultConstructorMarker.reference).object); + jni.JString getCanonicalPath() { + return const jni.JStringType().fromRef(_getCanonicalPath(reference).object); } -} - -class $ScreenshotRecorderConfig_CompanionType - extends jni.JObjType { - const $ScreenshotRecorderConfig_CompanionType(); - @override - String get signature => - r"Lio/sentry/android/replay/ScreenshotRecorderConfig$Companion;"; + static final _getCanonicalFile = jniLookup< + ffi + .NativeFunction)>>( + "File__getCanonicalFile") + .asFunction)>(); - @override - ScreenshotRecorderConfig_Companion fromRef(jni.JObjectPtr ref) => - ScreenshotRecorderConfig_Companion.fromRef(ref); + /// from: public java.io.File getCanonicalFile() + /// The returned object must be released after use, by calling the [release] method. + File getCanonicalFile() { + return const $FileType().fromRef(_getCanonicalFile(reference).object); + } - @override - jni.JObjType get superType => const jni.JObjectType(); + static final _toURL = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__toURL") + .asFunction)>(); - @override - final superCount = 1; + /// from: public java.net.URL toURL() + /// The returned object must be released after use, by calling the [release] method. + jni.JObject toURL() { + return const jni.JObjectType().fromRef(_toURL(reference).object); + } - @override - int get hashCode => ($ScreenshotRecorderConfig_CompanionType).hashCode; + static final _toURI = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__toURI") + .asFunction)>(); - @override - bool operator ==(Object other) { - return other.runtimeType == ($ScreenshotRecorderConfig_CompanionType) && - other is $ScreenshotRecorderConfig_CompanionType; + /// from: public java.net.URI toURI() + /// The returned object must be released after use, by calling the [release] method. + jni.JObject toURI() { + return const jni.JObjectType().fromRef(_toURI(reference).object); } -} -/// from: io.sentry.android.replay.ScreenshotRecorderConfig -class ScreenshotRecorderConfig extends jni.JObject { - @override - late final jni.JObjType $type = type; + static final _canRead = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__canRead") + .asFunction)>(); - ScreenshotRecorderConfig.fromRef( - jni.JObjectPtr ref, - ) : super.fromRef(ref); + /// from: public boolean canRead() + bool canRead() { + return _canRead(reference).boolean; + } - /// The type which includes information such as the signature of this class. - static const type = $ScreenshotRecorderConfigType(); - static final _get_Companion = - jniLookup>( - "get_ScreenshotRecorderConfig__Companion") - .asFunction(); + static final _canWrite = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__canWrite") + .asFunction)>(); - /// from: static public final io.sentry.android.replay.ScreenshotRecorderConfig$Companion Companion - /// The returned object must be released after use, by calling the [release] method. - static ScreenshotRecorderConfig_Companion get Companion => - const $ScreenshotRecorderConfig_CompanionType() - .fromRef(_get_Companion().object); + /// from: public boolean canWrite() + bool canWrite() { + return _canWrite(reference).boolean; + } - static final _new0 = jniLookup< + static final _exists = jniLookup< ffi.NativeFunction< - jni.JniResult Function(ffi.Int32, ffi.Int32, ffi.Float, ffi.Float, - ffi.Int32, ffi.Int32)>>("ScreenshotRecorderConfig__new0") - .asFunction(); + jni.JniResult Function(ffi.Pointer)>>("File__exists") + .asFunction)>(); - /// from: public void (int i, int i1, float f, float f1, int i2, int i3) - /// The returned object must be released after use, by calling the [release] method. - factory ScreenshotRecorderConfig( - int i, - int i1, - double f, - double f1, - int i2, - int i3, - ) { - return ScreenshotRecorderConfig.fromRef(_new0(i, i1, f, f1, i2, i3).object); + /// from: public boolean exists() + bool exists() { + return _exists(reference).boolean; } - static final _getRecordingWidth = jniLookup< + static final _isDirectory = jniLookup< ffi .NativeFunction)>>( - "ScreenshotRecorderConfig__getRecordingWidth") + "File__isDirectory") .asFunction)>(); - /// from: public final int getRecordingWidth() - int getRecordingWidth() { - return _getRecordingWidth(reference).integer; + /// from: public boolean isDirectory() + bool isDirectory() { + return _isDirectory(reference).boolean; } - static final _getRecordingHeight = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__getRecordingHeight") + static final _isFile = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__isFile") .asFunction)>(); - /// from: public final int getRecordingHeight() - int getRecordingHeight() { - return _getRecordingHeight(reference).integer; + /// from: public boolean isFile() + bool isFile() { + return _isFile(reference).boolean; } - static final _getScaleFactorX = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__getScaleFactorX") + static final _isHidden = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__isHidden") .asFunction)>(); - /// from: public final float getScaleFactorX() - double getScaleFactorX() { - return _getScaleFactorX(reference).float; + /// from: public boolean isHidden() + bool isHidden() { + return _isHidden(reference).boolean; } - static final _getScaleFactorY = jniLookup< + static final _lastModified = jniLookup< ffi .NativeFunction)>>( - "ScreenshotRecorderConfig__getScaleFactorY") + "File__lastModified") .asFunction)>(); - /// from: public final float getScaleFactorY() - double getScaleFactorY() { - return _getScaleFactorY(reference).float; + /// from: public long lastModified() + int lastModified() { + return _lastModified(reference).long; } - static final _getFrameRate = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__getFrameRate") + static final _length = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__length") .asFunction)>(); - /// from: public final int getFrameRate() - int getFrameRate() { - return _getFrameRate(reference).integer; + /// from: public long length() + int length() { + return _length(reference).long; } - static final _getBitRate = jniLookup< + static final _createNewFile = jniLookup< ffi .NativeFunction)>>( - "ScreenshotRecorderConfig__getBitRate") + "File__createNewFile") .asFunction)>(); - /// from: public final int getBitRate() - int getBitRate() { - return _getBitRate(reference).integer; + /// from: public boolean createNewFile() + bool createNewFile() { + return _createNewFile(reference).boolean; } - static final _component1 = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__component1") + static final _delete = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__delete") .asFunction)>(); - /// from: public final int component1() - int component1() { - return _component1(reference).integer; + /// from: public boolean delete() + bool delete() { + return _delete(reference).boolean; } - static final _component2 = jniLookup< + static final _deleteOnExit = jniLookup< ffi .NativeFunction)>>( - "ScreenshotRecorderConfig__component2") + "File__deleteOnExit") .asFunction)>(); - /// from: public final int component2() - int component2() { - return _component2(reference).integer; + /// from: public void deleteOnExit() + void deleteOnExit() { + return _deleteOnExit(reference).check(); } - static final _component3 = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__component3") + static final _list = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__list") .asFunction)>(); - /// from: public final float component3() - double component3() { - return _component3(reference).float; + /// from: public java.lang.String[] list() + /// The returned object must be released after use, by calling the [release] method. + jni.JArray list() { + return const jni.JArrayType(jni.JStringType()) + .fromRef(_list(reference).object); } - static final _component4 = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__component4") - .asFunction)>(); + static final _list1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>>("File__list1") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); - /// from: public final float component4() - double component4() { - return _component4(reference).float; + /// from: public java.lang.String[] list(java.io.FilenameFilter filenameFilter) + /// The returned object must be released after use, by calling the [release] method. + jni.JArray list1( + jni.JObject filenameFilter, + ) { + return const jni.JArrayType(jni.JStringType()) + .fromRef(_list1(reference, filenameFilter.reference).object); } - static final _component5 = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__component5") + static final _listFiles = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__listFiles") .asFunction)>(); - /// from: public final int component5() - int component5() { - return _component5(reference).integer; + /// from: public java.io.File[] listFiles() + /// The returned object must be released after use, by calling the [release] method. + jni.JArray listFiles() { + return const jni.JArrayType($FileType()) + .fromRef(_listFiles(reference).object); } - static final _component6 = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__component6") - .asFunction)>(); + static final _listFiles1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("File__listFiles1") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); - /// from: public final int component6() - int component6() { - return _component6(reference).integer; + /// from: public java.io.File[] listFiles(java.io.FilenameFilter filenameFilter) + /// The returned object must be released after use, by calling the [release] method. + jni.JArray listFiles1( + jni.JObject filenameFilter, + ) { + return const jni.JArrayType($FileType()) + .fromRef(_listFiles1(reference, filenameFilter.reference).object); } - static final _copy = jniLookup< + static final _listFiles2 = jniLookup< ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, - ffi.Int32, - ffi.Int32, - ffi.Float, - ffi.Float, - ffi.Int32, - ffi.Int32)>>("ScreenshotRecorderConfig__copy") + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("File__listFiles2") .asFunction< jni.JniResult Function( - ffi.Pointer, int, int, double, double, int, int)>(); + ffi.Pointer, ffi.Pointer)>(); - /// from: public final io.sentry.android.replay.ScreenshotRecorderConfig copy(int i, int i1, float f, float f1, int i2, int i3) + /// from: public java.io.File[] listFiles(java.io.FileFilter fileFilter) /// The returned object must be released after use, by calling the [release] method. - ScreenshotRecorderConfig copy( - int i, - int i1, - double f, - double f1, - int i2, - int i3, + jni.JArray listFiles2( + jni.JObject fileFilter, ) { - return const $ScreenshotRecorderConfigType() - .fromRef(_copy(reference, i, i1, f, f1, i2, i3).object); + return const jni.JArrayType($FileType()) + .fromRef(_listFiles2(reference, fileFilter.reference).object); } - static final _toString1 = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__toString1") + static final _mkdir = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__mkdir") .asFunction)>(); - /// from: public java.lang.String toString() - /// The returned object must be released after use, by calling the [release] method. - jni.JString toString1() { - return const jni.JStringType().fromRef(_toString1(reference).object); + /// from: public boolean mkdir() + bool mkdir() { + return _mkdir(reference).boolean; } - static final _hashCode1 = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__hashCode1") + static final _mkdirs = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__mkdirs") .asFunction)>(); - /// from: public int hashCode() - int hashCode1() { - return _hashCode1(reference).integer; + /// from: public boolean mkdirs() + bool mkdirs() { + return _mkdirs(reference).boolean; } - static final _equals1 = jniLookup< + static final _renameTo = jniLookup< ffi.NativeFunction< jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("ScreenshotRecorderConfig__equals1") + ffi.Pointer)>>("File__renameTo") .asFunction< jni.JniResult Function( ffi.Pointer, ffi.Pointer)>(); - /// from: public boolean equals(java.lang.Object object) - bool equals1( - jni.JObject object, + /// from: public boolean renameTo(java.io.File file) + bool renameTo( + File file, ) { - return _equals1(reference, object.reference).boolean; + return _renameTo(reference, file.reference).boolean; } -} - -class $ScreenshotRecorderConfigType - extends jni.JObjType { - const $ScreenshotRecorderConfigType(); - @override - String get signature => - r"Lio/sentry/android/replay/ScreenshotRecorderConfig;"; + static final _setLastModified = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Int64)>>("File__setLastModified") + .asFunction, int)>(); - @override - ScreenshotRecorderConfig fromRef(jni.JObjectPtr ref) => - ScreenshotRecorderConfig.fromRef(ref); + /// from: public boolean setLastModified(long j) + bool setLastModified( + int j, + ) { + return _setLastModified(reference, j).boolean; + } - @override - jni.JObjType get superType => const jni.JObjectType(); + static final _setReadOnly = jniLookup< + ffi + .NativeFunction)>>( + "File__setReadOnly") + .asFunction)>(); - @override - final superCount = 1; + /// from: public boolean setReadOnly() + bool setReadOnly() { + return _setReadOnly(reference).boolean; + } - @override - int get hashCode => ($ScreenshotRecorderConfigType).hashCode; + static final _setWritable = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, ffi.Uint8, + ffi.Uint8)>>("File__setWritable") + .asFunction, int, int)>(); - @override - bool operator ==(Object other) { - return other.runtimeType == ($ScreenshotRecorderConfigType) && - other is $ScreenshotRecorderConfigType; + /// from: public boolean setWritable(boolean z, boolean z1) + bool setWritable( + bool z, + bool z1, + ) { + return _setWritable(reference, z ? 1 : 0, z1 ? 1 : 0).boolean; } -} -/// from: io.sentry.android.replay.ReplayIntegration -class ReplayIntegration extends jni.JObject { - @override - late final jni.JObjType $type = type; + static final _setWritable1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Uint8)>>("File__setWritable1") + .asFunction, int)>(); - ReplayIntegration.fromRef( - jni.JObjectPtr ref, - ) : super.fromRef(ref); + /// from: public boolean setWritable(boolean z) + bool setWritable1( + bool z, + ) { + return _setWritable1(reference, z ? 1 : 0).boolean; + } - /// The type which includes information such as the signature of this class. - static const type = $ReplayIntegrationType(); - static final _new0 = jniLookup< + static final _setReadable = jniLookup< ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer)>>("ReplayIntegration__new0") - .asFunction< - jni.JniResult Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer)>(); + jni.JniResult Function(ffi.Pointer, ffi.Uint8, + ffi.Uint8)>>("File__setReadable") + .asFunction, int, int)>(); - /// from: public void (android.content.Context context, io.sentry.transport.ICurrentDateProvider iCurrentDateProvider, kotlin.jvm.functions.Function0 function0, kotlin.jvm.functions.Function1 function1, kotlin.jvm.functions.Function1 function11) - /// The returned object must be released after use, by calling the [release] method. - factory ReplayIntegration( - jni.JObject context, - jni.JObject iCurrentDateProvider, - jni.JObject function0, - jni.JObject function1, - jni.JObject function11, + /// from: public boolean setReadable(boolean z, boolean z1) + bool setReadable( + bool z, + bool z1, ) { - return ReplayIntegration.fromRef(_new0( - context.reference, - iCurrentDateProvider.reference, - function0.reference, - function1.reference, - function11.reference) - .object); + return _setReadable(reference, z ? 1 : 0, z1 ? 1 : 0).boolean; } - static final _new1 = jniLookup< + static final _setReadable1 = jniLookup< ffi.NativeFunction< jni.JniResult Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Int32, - ffi.Pointer)>>("ReplayIntegration__new1") - .asFunction< - jni.JniResult Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - int, - ffi.Pointer)>(); + ffi.Pointer, ffi.Uint8)>>("File__setReadable1") + .asFunction, int)>(); - /// from: public void (android.content.Context context, io.sentry.transport.ICurrentDateProvider iCurrentDateProvider, kotlin.jvm.functions.Function0 function0, kotlin.jvm.functions.Function1 function1, kotlin.jvm.functions.Function1 function11, int i, kotlin.jvm.internal.DefaultConstructorMarker defaultConstructorMarker) - /// The returned object must be released after use, by calling the [release] method. - factory ReplayIntegration.new1( - jni.JObject context, - jni.JObject iCurrentDateProvider, - jni.JObject function0, - jni.JObject function1, - jni.JObject function11, - int i, - jni.JObject defaultConstructorMarker, + /// from: public boolean setReadable(boolean z) + bool setReadable1( + bool z, + ) { + return _setReadable1(reference, z ? 1 : 0).boolean; + } + + static final _setExecutable = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, ffi.Uint8, + ffi.Uint8)>>("File__setExecutable") + .asFunction, int, int)>(); + + /// from: public boolean setExecutable(boolean z, boolean z1) + bool setExecutable( + bool z, + bool z1, ) { - return ReplayIntegration.fromRef(_new1( - context.reference, - iCurrentDateProvider.reference, - function0.reference, - function1.reference, - function11.reference, - i, - defaultConstructorMarker.reference) - .object); + return _setExecutable(reference, z ? 1 : 0, z1 ? 1 : 0).boolean; } - static final _new2 = jniLookup< + static final _setExecutable1 = jniLookup< ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("ReplayIntegration__new2") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); + jni.JniResult Function( + ffi.Pointer, ffi.Uint8)>>("File__setExecutable1") + .asFunction, int)>(); - /// from: public void (android.content.Context context, io.sentry.transport.ICurrentDateProvider iCurrentDateProvider) - /// The returned object must be released after use, by calling the [release] method. - factory ReplayIntegration.new2( - jni.JObject context, - jni.JObject iCurrentDateProvider, + /// from: public boolean setExecutable(boolean z) + bool setExecutable1( + bool z, ) { - return ReplayIntegration.fromRef( - _new2(context.reference, iCurrentDateProvider.reference).object); + return _setExecutable1(reference, z ? 1 : 0).boolean; } - static final _getReplayCacheDir = jniLookup< + static final _canExecute = jniLookup< ffi .NativeFunction)>>( - "ReplayIntegration__getReplayCacheDir") + "File__canExecute") .asFunction)>(); - /// from: public final java.io.File getReplayCacheDir() - /// The returned object must be released after use, by calling the [release] method. - File getReplayCacheDir() { - return const $FileType().fromRef(_getReplayCacheDir(reference).object); + /// from: public boolean canExecute() + bool canExecute() { + return _canExecute(reference).boolean; } - static final _register = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer)>>("ReplayIntegration__register") - .asFunction< - jni.JniResult Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer)>(); + static final _listRoots = + jniLookup>("File__listRoots") + .asFunction(); - /// from: public void register(io.sentry.IHub iHub, io.sentry.SentryOptions sentryOptions) - void register( - jni.JObject iHub, - jni.JObject sentryOptions, - ) { - return _register(reference, iHub.reference, sentryOptions.reference) - .check(); + /// from: static public java.io.File[] listRoots() + /// The returned object must be released after use, by calling the [release] method. + static jni.JArray listRoots() { + return const jni.JArrayType($FileType()).fromRef(_listRoots().object); } - static final _isRecording = jniLookup< + static final _getTotalSpace = jniLookup< ffi .NativeFunction)>>( - "ReplayIntegration__isRecording") + "File__getTotalSpace") .asFunction)>(); - /// from: public boolean isRecording() - bool isRecording() { - return _isRecording(reference).boolean; + /// from: public long getTotalSpace() + int getTotalSpace() { + return _getTotalSpace(reference).long; } - static final _start = jniLookup< + static final _getFreeSpace = jniLookup< ffi .NativeFunction)>>( - "ReplayIntegration__start") + "File__getFreeSpace") .asFunction)>(); - /// from: public void start() - void start() { - return _start(reference).check(); + /// from: public long getFreeSpace() + int getFreeSpace() { + return _getFreeSpace(reference).long; } - static final _resume = jniLookup< + static final _getUsableSpace = jniLookup< ffi .NativeFunction)>>( - "ReplayIntegration__resume") + "File__getUsableSpace") .asFunction)>(); - /// from: public void resume() - void resume() { - return _resume(reference).check(); + /// from: public long getUsableSpace() + int getUsableSpace() { + return _getUsableSpace(reference).long; } - static final _sendReplayForEvent = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer, ffi.Pointer)>>( - "ReplayIntegration__sendReplayForEvent") + static final _createTempFile = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>>("File__createTempFile") .asFunction< jni.JniResult Function(ffi.Pointer, ffi.Pointer, ffi.Pointer)>(); - /// from: public void sendReplayForEvent(io.sentry.SentryEvent sentryEvent, io.sentry.Hint hint) - void sendReplayForEvent( - jni.JObject sentryEvent, - jni.JObject hint, + /// from: static public java.io.File createTempFile(java.lang.String string, java.lang.String string1, java.io.File file) + /// The returned object must be released after use, by calling the [release] method. + static File createTempFile( + jni.JString string, + jni.JString string1, + File file, ) { - return _sendReplayForEvent(reference, sentryEvent.reference, hint.reference) - .check(); + return const $FileType().fromRef( + _createTempFile(string.reference, string1.reference, file.reference) + .object); } - static final _getReplayId = jniLookup< - ffi - .NativeFunction)>>( - "ReplayIntegration__getReplayId") - .asFunction)>(); + static final _createTempFile1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("File__createTempFile1") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); - /// from: public io.sentry.protocol.SentryId getReplayId() + /// from: static public java.io.File createTempFile(java.lang.String string, java.lang.String string1) /// The returned object must be released after use, by calling the [release] method. - jni.JObject getReplayId() { - return const jni.JObjectType().fromRef(_getReplayId(reference).object); - } - - static final _pause = jniLookup< - ffi - .NativeFunction)>>( - "ReplayIntegration__pause") - .asFunction)>(); - - /// from: public void pause() - void pause() { - return _pause(reference).check(); - } - - static final _stop = jniLookup< - ffi - .NativeFunction)>>( - "ReplayIntegration__stop") - .asFunction)>(); - - /// from: public void stop() - void stop() { - return _stop(reference).check(); + static File createTempFile1( + jni.JString string, + jni.JString string1, + ) { + return const $FileType() + .fromRef(_createTempFile1(string.reference, string1.reference).object); } - static final _onScreenshotRecorded = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>>( - "ReplayIntegration__onScreenshotRecorded") + static final _compareTo = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("File__compareTo") .asFunction< jni.JniResult Function( ffi.Pointer, ffi.Pointer)>(); - /// from: public void onScreenshotRecorded(android.graphics.Bitmap bitmap) - void onScreenshotRecorded( - jni.JObject bitmap, + /// from: public int compareTo(java.io.File file) + int compareTo( + File file, ) { - return _onScreenshotRecorded(reference, bitmap.reference).check(); + return _compareTo(reference, file.reference).integer; } - static final _onScreenshotRecorded1 = jniLookup< + static final _equals1 = jniLookup< ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, - ffi.Pointer, - ffi.Int64)>>("ReplayIntegration__onScreenshotRecorded1") + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("File__equals1") .asFunction< jni.JniResult Function( - ffi.Pointer, ffi.Pointer, int)>(); + ffi.Pointer, ffi.Pointer)>(); - /// from: public void onScreenshotRecorded(java.io.File file, long j) - void onScreenshotRecorded1( - File file, - int j, + /// from: public boolean equals(java.lang.Object object) + bool equals1( + jni.JObject object, ) { - return _onScreenshotRecorded1(reference, file.reference, j).check(); + return _equals1(reference, object.reference).boolean; } - static final _close = jniLookup< - ffi - .NativeFunction)>>( - "ReplayIntegration__close") + static final _hashCode1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__hashCode1") .asFunction)>(); - /// from: public void close() - void close() { - return _close(reference).check(); + /// from: public int hashCode() + int hashCode1() { + return _hashCode1(reference).integer; } - static final _onConfigurationChanged = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>>( - "ReplayIntegration__onConfigurationChanged") + static final _toString1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__toString1") + .asFunction)>(); + + /// from: public java.lang.String toString() + /// The returned object must be released after use, by calling the [release] method. + jni.JString toString1() { + return const jni.JStringType().fromRef(_toString1(reference).object); + } + + static final _toPath = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("File__toPath") + .asFunction)>(); + + /// from: public java.nio.file.Path toPath() + /// The returned object must be released after use, by calling the [release] method. + jni.JObject toPath() { + return const jni.JObjectType().fromRef(_toPath(reference).object); + } + + static final _compareTo1 = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("File__compareTo1") .asFunction< jni.JniResult Function( ffi.Pointer, ffi.Pointer)>(); - /// from: public void onConfigurationChanged(android.content.res.Configuration configuration) - void onConfigurationChanged( - jni.JObject configuration, + /// from: public int compareTo(java.lang.Object object) + int compareTo1( + jni.JObject object, ) { - return _onConfigurationChanged(reference, configuration.reference).check(); - } - - static final _onLowMemory = jniLookup< - ffi - .NativeFunction)>>( - "ReplayIntegration__onLowMemory") - .asFunction)>(); - - /// from: public void onLowMemory() - void onLowMemory() { - return _onLowMemory(reference).check(); + return _compareTo1(reference, object.reference).integer; } } -class $ReplayIntegrationType extends jni.JObjType { - const $ReplayIntegrationType(); +class $FileType extends jni.JObjType { + const $FileType(); @override - String get signature => r"Lio/sentry/android/replay/ReplayIntegration;"; + String get signature => r"Ljava/io/File;"; @override - ReplayIntegration fromRef(jni.JObjectPtr ref) => - ReplayIntegration.fromRef(ref); + File fromRef(jni.JObjectPtr ref) => File.fromRef(ref); @override jni.JObjType get superType => const jni.JObjectType(); @@ -894,12 +775,11 @@ class $ReplayIntegrationType extends jni.JObjType { final superCount = 1; @override - int get hashCode => ($ReplayIntegrationType).hashCode; + int get hashCode => ($FileType).hashCode; @override bool operator ==(Object other) { - return other.runtimeType == ($ReplayIntegrationType) && - other is $ReplayIntegrationType; + return other.runtimeType == ($FileType) && other is $FileType; } } @@ -985,48 +865,268 @@ class SentryFlutterReplay extends jni.JObject { jni.JniResult Function( ffi.Pointer, ffi.Pointer)>(); - /// from: public final void setRecorder(io.sentry.android.replay.Recorder recorder) - void setRecorder( - Recorder recorder, - ) { - return _setRecorder(reference, recorder.reference).check(); + /// from: public final void setRecorder(io.sentry.android.replay.Recorder recorder) + void setRecorder( + Recorder recorder, + ) { + return _setRecorder(reference, recorder.reference).check(); + } + + static final _getIntegration = jniLookup< + ffi + .NativeFunction)>>( + "SentryFlutterReplay__getIntegration") + .asFunction)>(); + + /// from: public final io.sentry.android.replay.ReplayIntegration getIntegration() + /// The returned object must be released after use, by calling the [release] method. + ReplayIntegration getIntegration() { + return const $ReplayIntegrationType() + .fromRef(_getIntegration(reference).object); + } + + static final _setIntegration = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>>( + "SentryFlutterReplay__setIntegration") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public final void setIntegration(io.sentry.android.replay.ReplayIntegration replayIntegration) + void setIntegration( + ReplayIntegration replayIntegration, + ) { + return _setIntegration(reference, replayIntegration.reference).check(); + } +} + +class $SentryFlutterReplayType extends jni.JObjType { + const $SentryFlutterReplayType(); + + @override + String get signature => r"Lio/sentry/flutter/SentryFlutterReplay;"; + + @override + SentryFlutterReplay fromRef(jni.JObjectPtr ref) => + SentryFlutterReplay.fromRef(ref); + + @override + jni.JObjType get superType => const jni.JObjectType(); + + @override + final superCount = 1; + + @override + int get hashCode => ($SentryFlutterReplayType).hashCode; + + @override + bool operator ==(Object other) { + return other.runtimeType == ($SentryFlutterReplayType) && + other is $SentryFlutterReplayType; + } +} + +/// from: io.sentry.android.replay.Recorder +class Recorder extends jni.JObject { + @override + late final jni.JObjType $type = type; + + Recorder.fromRef( + jni.JObjectPtr ref, + ) : super.fromRef(ref); + + /// The type which includes information such as the signature of this class. + static const type = $RecorderType(); + static final _start = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("Recorder__start") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); + + /// from: public abstract void start(io.sentry.android.replay.ScreenshotRecorderConfig screenshotRecorderConfig) + void start( + ScreenshotRecorderConfig screenshotRecorderConfig, + ) { + return _start(reference, screenshotRecorderConfig.reference).check(); + } + + static final _resume = jniLookup< + ffi + .NativeFunction)>>( + "Recorder__resume") + .asFunction)>(); + + /// from: public abstract void resume() + void resume() { + return _resume(reference).check(); + } + + static final _pause = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("Recorder__pause") + .asFunction)>(); + + /// from: public abstract void pause() + void pause() { + return _pause(reference).check(); + } + + static final _stop = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer)>>("Recorder__stop") + .asFunction)>(); + + /// from: public abstract void stop() + void stop() { + return _stop(reference).check(); + } + + /// Maps a specific port to the implemented interface. + static final Map _$impls = {}; + ReceivePort? _$p; + + static jni.JObjectPtr _$invoke( + int port, + jni.JObjectPtr descriptor, + jni.JObjectPtr args, + ) { + return _$invokeMethod( + port, + $MethodInvocation.fromAddresses( + 0, + descriptor.address, + args.address, + ), + ); + } + + static final ffi.Pointer< + ffi.NativeFunction< + jni.JObjectPtr Function( + ffi.Uint64, jni.JObjectPtr, jni.JObjectPtr)>> + _$invokePointer = ffi.Pointer.fromFunction(_$invoke); + + static ffi.Pointer _$invokeMethod( + int $p, + $MethodInvocation $i, + ) { + try { + final $d = $i.methodDescriptor.toDartString(releaseOriginal: true); + final $a = $i.args; + if ($d == + r"start(Lio/sentry/android/replay/ScreenshotRecorderConfig;)V") { + _$impls[$p]!.start( + $a[0].castTo(const $ScreenshotRecorderConfigType(), + releaseOriginal: true), + ); + return jni.nullptr; + } + if ($d == r"resume()V") { + _$impls[$p]!.resume(); + return jni.nullptr; + } + if ($d == r"pause()V") { + _$impls[$p]!.pause(); + return jni.nullptr; + } + if ($d == r"stop()V") { + _$impls[$p]!.stop(); + return jni.nullptr; + } + } catch (e) { + return ProtectedJniExtensions.newDartException(e.toString()); + } + return jni.nullptr; + } + + factory Recorder.implement( + $RecorderImpl $impl, + ) { + final $p = ReceivePort(); + final $x = Recorder.fromRef( + ProtectedJniExtensions.newPortProxy( + r"io.sentry.android.replay.Recorder", + $p, + _$invokePointer, + ), + ).._$p = $p; + final $a = $p.sendPort.nativePort; + _$impls[$a] = $impl; + $p.listen(($m) { + if ($m == null) { + _$impls.remove($p.sendPort.nativePort); + $p.close(); + return; + } + final $i = $MethodInvocation.fromMessage($m); + final $r = _$invokeMethod($p.sendPort.nativePort, $i); + ProtectedJniExtensions.returnResult($i.result, $r); + }); + return $x; + } +} + +abstract class $RecorderImpl { + factory $RecorderImpl({ + required void Function(ScreenshotRecorderConfig screenshotRecorderConfig) + start, + required void Function() resume, + required void Function() pause, + required void Function() stop, + }) = _$RecorderImpl; + + void start(ScreenshotRecorderConfig screenshotRecorderConfig); + void resume(); + void pause(); + void stop(); +} + +class _$RecorderImpl implements $RecorderImpl { + _$RecorderImpl({ + required void Function(ScreenshotRecorderConfig screenshotRecorderConfig) + start, + required void Function() resume, + required void Function() pause, + required void Function() stop, + }) : _start = start, + _resume = resume, + _pause = pause, + _stop = stop; + + final void Function(ScreenshotRecorderConfig screenshotRecorderConfig) _start; + final void Function() _resume; + final void Function() _pause; + final void Function() _stop; + + void start(ScreenshotRecorderConfig screenshotRecorderConfig) { + return _start(screenshotRecorderConfig); } - static final _getCacheDir = jniLookup< - ffi - .NativeFunction)>>( - "SentryFlutterReplay__getCacheDir") - .asFunction)>(); - - /// from: public final java.lang.String getCacheDir() - /// The returned object must be released after use, by calling the [release] method. - jni.JString getCacheDir() { - return const jni.JStringType().fromRef(_getCacheDir(reference).object); + void resume() { + return _resume(); } - static final _getCallbackObject = jniLookup< - ffi - .NativeFunction)>>( - "SentryFlutterReplay__getCallbackObject") - .asFunction)>(); + void pause() { + return _pause(); + } - /// from: public final io.sentry.android.replay.ScreenshotRecorderCallback getCallbackObject() - /// The returned object must be released after use, by calling the [release] method. - jni.JObject getCallbackObject() { - return const jni.JObjectType() - .fromRef(_getCallbackObject(reference).object); + void stop() { + return _stop(); } } -class $SentryFlutterReplayType extends jni.JObjType { - const $SentryFlutterReplayType(); +class $RecorderType extends jni.JObjType { + const $RecorderType(); @override - String get signature => r"Lio/sentry/flutter/SentryFlutterReplay;"; + String get signature => r"Lio/sentry/android/replay/Recorder;"; @override - SentryFlutterReplay fromRef(jni.JObjectPtr ref) => - SentryFlutterReplay.fromRef(ref); + Recorder fromRef(jni.JObjectPtr ref) => Recorder.fromRef(ref); @override jni.JObjType get superType => const jni.JObjectType(); @@ -1035,757 +1135,682 @@ class $SentryFlutterReplayType extends jni.JObjType { final superCount = 1; @override - int get hashCode => ($SentryFlutterReplayType).hashCode; + int get hashCode => ($RecorderType).hashCode; @override bool operator ==(Object other) { - return other.runtimeType == ($SentryFlutterReplayType) && - other is $SentryFlutterReplayType; + return other.runtimeType == ($RecorderType) && other is $RecorderType; } } -/// from: java.io.File -class File extends jni.JObject { +/// from: io.sentry.android.replay.ScreenshotRecorderConfig$Companion +class ScreenshotRecorderConfig_Companion extends jni.JObject { @override - late final jni.JObjType $type = type; + late final jni.JObjType $type = type; - File.fromRef( + ScreenshotRecorderConfig_Companion.fromRef( jni.JObjectPtr ref, ) : super.fromRef(ref); /// The type which includes information such as the signature of this class. - static const type = $FileType(); - static final _get_pathSeparator = - jniLookup>( - "get_File__pathSeparator") - .asFunction(); - - /// from: static public final java.lang.String pathSeparator - /// The returned object must be released after use, by calling the [release] method. - static jni.JString get pathSeparator => - const jni.JStringType().fromRef(_get_pathSeparator().object); - - static final _get_pathSeparatorChar = - jniLookup>( - "get_File__pathSeparatorChar") - .asFunction(); - - /// from: static public final char pathSeparatorChar - static int get pathSeparatorChar => _get_pathSeparatorChar().char; - - static final _get_separator = - jniLookup>( - "get_File__separator") - .asFunction(); - - /// from: static public final java.lang.String separator - /// The returned object must be released after use, by calling the [release] method. - static jni.JString get separator => - const jni.JStringType().fromRef(_get_separator().object); - - static final _get_separatorChar = - jniLookup>( - "get_File__separatorChar") - .asFunction(); - - /// from: static public final char separatorChar - static int get separatorChar => _get_separatorChar().char; - - static final _new0 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__new0") - .asFunction)>(); - - /// from: public void (java.lang.String string) - /// The returned object must be released after use, by calling the [release] method. - factory File( - jni.JString string, - ) { - return File.fromRef(_new0(string.reference).object); - } - - static final _new1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>>("File__new1") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public void (java.lang.String string, java.lang.String string1) - /// The returned object must be released after use, by calling the [release] method. - factory File.new1( - jni.JString string, - jni.JString string1, - ) { - return File.fromRef(_new1(string.reference, string1.reference).object); - } - - static final _new2 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>>("File__new2") + static const type = $ScreenshotRecorderConfig_CompanionType(); + static final _from = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer, ffi.Pointer)>>( + "ScreenshotRecorderConfig_Companion__from") .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public void (java.io.File file, java.lang.String string) - /// The returned object must be released after use, by calling the [release] method. - factory File.new2( - File file, - jni.JString string, - ) { - return File.fromRef(_new2(file.reference, string.reference).object); - } - - static final _new3 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__new3") - .asFunction)>(); + jni.JniResult Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); - /// from: public void (java.net.URI uRI) + /// from: public final io.sentry.android.replay.ScreenshotRecorderConfig from(android.content.Context context, io.sentry.SentryReplayOptions sentryReplayOptions) /// The returned object must be released after use, by calling the [release] method. - factory File.new3( - jni.JObject uRI, + ScreenshotRecorderConfig from( + jni.JObject context, + jni.JObject sentryReplayOptions, ) { - return File.fromRef(_new3(uRI.reference).object); - } - - static final _getName = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__getName") - .asFunction)>(); - - /// from: public java.lang.String getName() - /// The returned object must be released after use, by calling the [release] method. - jni.JString getName() { - return const jni.JStringType().fromRef(_getName(reference).object); - } - - static final _getParent = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__getParent") - .asFunction)>(); - - /// from: public java.lang.String getParent() - /// The returned object must be released after use, by calling the [release] method. - jni.JString getParent() { - return const jni.JStringType().fromRef(_getParent(reference).object); - } - - static final _getParentFile = jniLookup< - ffi - .NativeFunction)>>( - "File__getParentFile") - .asFunction)>(); - - /// from: public java.io.File getParentFile() - /// The returned object must be released after use, by calling the [release] method. - File getParentFile() { - return const $FileType().fromRef(_getParentFile(reference).object); - } - - static final _getPath = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__getPath") - .asFunction)>(); - - /// from: public java.lang.String getPath() - /// The returned object must be released after use, by calling the [release] method. - jni.JString getPath() { - return const jni.JStringType().fromRef(_getPath(reference).object); - } - - static final _isAbsolute = jniLookup< - ffi - .NativeFunction)>>( - "File__isAbsolute") - .asFunction)>(); - - /// from: public boolean isAbsolute() - bool isAbsolute() { - return _isAbsolute(reference).boolean; - } - - static final _getAbsolutePath = jniLookup< - ffi - .NativeFunction)>>( - "File__getAbsolutePath") - .asFunction)>(); - - /// from: public java.lang.String getAbsolutePath() - /// The returned object must be released after use, by calling the [release] method. - jni.JString getAbsolutePath() { - return const jni.JStringType().fromRef(_getAbsolutePath(reference).object); + return const $ScreenshotRecorderConfigType().fromRef( + _from(reference, context.reference, sentryReplayOptions.reference) + .object); } - static final _getAbsoluteFile = jniLookup< + static final _new0 = jniLookup< ffi .NativeFunction)>>( - "File__getAbsoluteFile") + "ScreenshotRecorderConfig_Companion__new0") .asFunction)>(); - /// from: public java.io.File getAbsoluteFile() + /// from: public void (kotlin.jvm.internal.DefaultConstructorMarker defaultConstructorMarker) /// The returned object must be released after use, by calling the [release] method. - File getAbsoluteFile() { - return const $FileType().fromRef(_getAbsoluteFile(reference).object); + factory ScreenshotRecorderConfig_Companion( + jni.JObject defaultConstructorMarker, + ) { + return ScreenshotRecorderConfig_Companion.fromRef( + _new0(defaultConstructorMarker.reference).object); } +} - static final _getCanonicalPath = jniLookup< - ffi - .NativeFunction)>>( - "File__getCanonicalPath") - .asFunction)>(); - - /// from: public java.lang.String getCanonicalPath() - /// The returned object must be released after use, by calling the [release] method. - jni.JString getCanonicalPath() { - return const jni.JStringType().fromRef(_getCanonicalPath(reference).object); - } +class $ScreenshotRecorderConfig_CompanionType + extends jni.JObjType { + const $ScreenshotRecorderConfig_CompanionType(); - static final _getCanonicalFile = jniLookup< - ffi - .NativeFunction)>>( - "File__getCanonicalFile") - .asFunction)>(); + @override + String get signature => + r"Lio/sentry/android/replay/ScreenshotRecorderConfig$Companion;"; - /// from: public java.io.File getCanonicalFile() - /// The returned object must be released after use, by calling the [release] method. - File getCanonicalFile() { - return const $FileType().fromRef(_getCanonicalFile(reference).object); - } + @override + ScreenshotRecorderConfig_Companion fromRef(jni.JObjectPtr ref) => + ScreenshotRecorderConfig_Companion.fromRef(ref); - static final _toURL = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__toURL") - .asFunction)>(); + @override + jni.JObjType get superType => const jni.JObjectType(); - /// from: public java.net.URL toURL() - /// The returned object must be released after use, by calling the [release] method. - jni.JObject toURL() { - return const jni.JObjectType().fromRef(_toURL(reference).object); - } + @override + final superCount = 1; - static final _toURI = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__toURI") - .asFunction)>(); + @override + int get hashCode => ($ScreenshotRecorderConfig_CompanionType).hashCode; - /// from: public java.net.URI toURI() - /// The returned object must be released after use, by calling the [release] method. - jni.JObject toURI() { - return const jni.JObjectType().fromRef(_toURI(reference).object); + @override + bool operator ==(Object other) { + return other.runtimeType == ($ScreenshotRecorderConfig_CompanionType) && + other is $ScreenshotRecorderConfig_CompanionType; } +} - static final _canRead = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__canRead") - .asFunction)>(); +/// from: io.sentry.android.replay.ScreenshotRecorderConfig +class ScreenshotRecorderConfig extends jni.JObject { + @override + late final jni.JObjType $type = type; - /// from: public boolean canRead() - bool canRead() { - return _canRead(reference).boolean; - } + ScreenshotRecorderConfig.fromRef( + jni.JObjectPtr ref, + ) : super.fromRef(ref); - static final _canWrite = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__canWrite") - .asFunction)>(); + /// The type which includes information such as the signature of this class. + static const type = $ScreenshotRecorderConfigType(); + static final _get_Companion = + jniLookup>( + "get_ScreenshotRecorderConfig__Companion") + .asFunction(); - /// from: public boolean canWrite() - bool canWrite() { - return _canWrite(reference).boolean; - } + /// from: static public final io.sentry.android.replay.ScreenshotRecorderConfig$Companion Companion + /// The returned object must be released after use, by calling the [release] method. + static ScreenshotRecorderConfig_Companion get Companion => + const $ScreenshotRecorderConfig_CompanionType() + .fromRef(_get_Companion().object); - static final _exists = jniLookup< + static final _new0 = jniLookup< ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__exists") - .asFunction)>(); + jni.JniResult Function(ffi.Int32, ffi.Int32, ffi.Float, ffi.Float, + ffi.Int32, ffi.Int32)>>("ScreenshotRecorderConfig__new0") + .asFunction(); - /// from: public boolean exists() - bool exists() { - return _exists(reference).boolean; + /// from: public void (int i, int i1, float f, float f1, int i2, int i3) + /// The returned object must be released after use, by calling the [release] method. + factory ScreenshotRecorderConfig( + int i, + int i1, + double f, + double f1, + int i2, + int i3, + ) { + return ScreenshotRecorderConfig.fromRef(_new0(i, i1, f, f1, i2, i3).object); } - static final _isDirectory = jniLookup< + static final _getRecordingWidth = jniLookup< ffi .NativeFunction)>>( - "File__isDirectory") + "ScreenshotRecorderConfig__getRecordingWidth") .asFunction)>(); - /// from: public boolean isDirectory() - bool isDirectory() { - return _isDirectory(reference).boolean; + /// from: public final int getRecordingWidth() + int getRecordingWidth() { + return _getRecordingWidth(reference).integer; } - static final _isFile = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__isFile") + static final _getRecordingHeight = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__getRecordingHeight") .asFunction)>(); - /// from: public boolean isFile() - bool isFile() { - return _isFile(reference).boolean; + /// from: public final int getRecordingHeight() + int getRecordingHeight() { + return _getRecordingHeight(reference).integer; } - static final _isHidden = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__isHidden") + static final _getScaleFactorX = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__getScaleFactorX") .asFunction)>(); - /// from: public boolean isHidden() - bool isHidden() { - return _isHidden(reference).boolean; + /// from: public final float getScaleFactorX() + double getScaleFactorX() { + return _getScaleFactorX(reference).float; } - static final _lastModified = jniLookup< + static final _getScaleFactorY = jniLookup< ffi .NativeFunction)>>( - "File__lastModified") + "ScreenshotRecorderConfig__getScaleFactorY") .asFunction)>(); - /// from: public long lastModified() - int lastModified() { - return _lastModified(reference).long; + /// from: public final float getScaleFactorY() + double getScaleFactorY() { + return _getScaleFactorY(reference).float; } - static final _length = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__length") + static final _getFrameRate = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__getFrameRate") .asFunction)>(); - /// from: public long length() - int length() { - return _length(reference).long; + /// from: public final int getFrameRate() + int getFrameRate() { + return _getFrameRate(reference).integer; } - static final _createNewFile = jniLookup< + static final _getBitRate = jniLookup< ffi .NativeFunction)>>( - "File__createNewFile") + "ScreenshotRecorderConfig__getBitRate") .asFunction)>(); - /// from: public boolean createNewFile() - bool createNewFile() { - return _createNewFile(reference).boolean; + /// from: public final int getBitRate() + int getBitRate() { + return _getBitRate(reference).integer; } - static final _delete = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__delete") + static final _component1 = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__component1") .asFunction)>(); - /// from: public boolean delete() - bool delete() { - return _delete(reference).boolean; + /// from: public final int component1() + int component1() { + return _component1(reference).integer; } - static final _deleteOnExit = jniLookup< + static final _component2 = jniLookup< ffi .NativeFunction)>>( - "File__deleteOnExit") + "ScreenshotRecorderConfig__component2") .asFunction)>(); - /// from: public void deleteOnExit() - void deleteOnExit() { - return _deleteOnExit(reference).check(); + /// from: public final int component2() + int component2() { + return _component2(reference).integer; } - static final _list = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__list") + static final _component3 = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__component3") .asFunction)>(); - /// from: public java.lang.String[] list() - /// The returned object must be released after use, by calling the [release] method. - jni.JArray list() { - return const jni.JArrayType(jni.JStringType()) - .fromRef(_list(reference).object); + /// from: public final float component3() + double component3() { + return _component3(reference).float; } - static final _list1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>>("File__list1") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); + static final _component4 = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__component4") + .asFunction)>(); - /// from: public java.lang.String[] list(java.io.FilenameFilter filenameFilter) - /// The returned object must be released after use, by calling the [release] method. - jni.JArray list1( - jni.JObject filenameFilter, - ) { - return const jni.JArrayType(jni.JStringType()) - .fromRef(_list1(reference, filenameFilter.reference).object); + /// from: public final float component4() + double component4() { + return _component4(reference).float; } - static final _listFiles = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__listFiles") + static final _component5 = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__component5") .asFunction)>(); - /// from: public java.io.File[] listFiles() - /// The returned object must be released after use, by calling the [release] method. - jni.JArray listFiles() { - return const jni.JArrayType($FileType()) - .fromRef(_listFiles(reference).object); + /// from: public final int component5() + int component5() { + return _component5(reference).integer; } - static final _listFiles1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("File__listFiles1") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); + static final _component6 = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__component6") + .asFunction)>(); - /// from: public java.io.File[] listFiles(java.io.FilenameFilter filenameFilter) - /// The returned object must be released after use, by calling the [release] method. - jni.JArray listFiles1( - jni.JObject filenameFilter, - ) { - return const jni.JArrayType($FileType()) - .fromRef(_listFiles1(reference, filenameFilter.reference).object); + /// from: public final int component6() + int component6() { + return _component6(reference).integer; } - static final _listFiles2 = jniLookup< + static final _copy = jniLookup< ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("File__listFiles2") + jni.JniResult Function( + ffi.Pointer, + ffi.Int32, + ffi.Int32, + ffi.Float, + ffi.Float, + ffi.Int32, + ffi.Int32)>>("ScreenshotRecorderConfig__copy") .asFunction< jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); + ffi.Pointer, int, int, double, double, int, int)>(); - /// from: public java.io.File[] listFiles(java.io.FileFilter fileFilter) + /// from: public final io.sentry.android.replay.ScreenshotRecorderConfig copy(int i, int i1, float f, float f1, int i2, int i3) /// The returned object must be released after use, by calling the [release] method. - jni.JArray listFiles2( - jni.JObject fileFilter, + ScreenshotRecorderConfig copy( + int i, + int i1, + double f, + double f1, + int i2, + int i3, ) { - return const jni.JArrayType($FileType()) - .fromRef(_listFiles2(reference, fileFilter.reference).object); + return const $ScreenshotRecorderConfigType() + .fromRef(_copy(reference, i, i1, f, f1, i2, i3).object); } - static final _mkdir = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__mkdir") + static final _toString1 = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__toString1") .asFunction)>(); - /// from: public boolean mkdir() - bool mkdir() { - return _mkdir(reference).boolean; + /// from: public java.lang.String toString() + /// The returned object must be released after use, by calling the [release] method. + jni.JString toString1() { + return const jni.JStringType().fromRef(_toString1(reference).object); } - static final _mkdirs = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__mkdirs") + static final _hashCode1 = jniLookup< + ffi + .NativeFunction)>>( + "ScreenshotRecorderConfig__hashCode1") .asFunction)>(); - /// from: public boolean mkdirs() - bool mkdirs() { - return _mkdirs(reference).boolean; + /// from: public int hashCode() + int hashCode1() { + return _hashCode1(reference).integer; } - static final _renameTo = jniLookup< + static final _equals1 = jniLookup< ffi.NativeFunction< jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("File__renameTo") + ffi.Pointer)>>("ScreenshotRecorderConfig__equals1") .asFunction< jni.JniResult Function( ffi.Pointer, ffi.Pointer)>(); - /// from: public boolean renameTo(java.io.File file) - bool renameTo( - File file, + /// from: public boolean equals(java.lang.Object object) + bool equals1( + jni.JObject object, ) { - return _renameTo(reference, file.reference).boolean; + return _equals1(reference, object.reference).boolean; } +} - static final _setLastModified = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Int64)>>("File__setLastModified") - .asFunction, int)>(); - - /// from: public boolean setLastModified(long j) - bool setLastModified( - int j, - ) { - return _setLastModified(reference, j).boolean; - } +class $ScreenshotRecorderConfigType + extends jni.JObjType { + const $ScreenshotRecorderConfigType(); - static final _setReadOnly = jniLookup< - ffi - .NativeFunction)>>( - "File__setReadOnly") - .asFunction)>(); + @override + String get signature => + r"Lio/sentry/android/replay/ScreenshotRecorderConfig;"; - /// from: public boolean setReadOnly() - bool setReadOnly() { - return _setReadOnly(reference).boolean; - } + @override + ScreenshotRecorderConfig fromRef(jni.JObjectPtr ref) => + ScreenshotRecorderConfig.fromRef(ref); - static final _setWritable = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, ffi.Uint8, - ffi.Uint8)>>("File__setWritable") - .asFunction, int, int)>(); + @override + jni.JObjType get superType => const jni.JObjectType(); - /// from: public boolean setWritable(boolean z, boolean z1) - bool setWritable( - bool z, - bool z1, - ) { - return _setWritable(reference, z ? 1 : 0, z1 ? 1 : 0).boolean; - } + @override + final superCount = 1; - static final _setWritable1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Uint8)>>("File__setWritable1") - .asFunction, int)>(); + @override + int get hashCode => ($ScreenshotRecorderConfigType).hashCode; - /// from: public boolean setWritable(boolean z) - bool setWritable1( - bool z, - ) { - return _setWritable1(reference, z ? 1 : 0).boolean; + @override + bool operator ==(Object other) { + return other.runtimeType == ($ScreenshotRecorderConfigType) && + other is $ScreenshotRecorderConfigType; } +} - static final _setReadable = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, ffi.Uint8, - ffi.Uint8)>>("File__setReadable") - .asFunction, int, int)>(); +/// from: io.sentry.android.replay.ReplayIntegration +class ReplayIntegration extends jni.JObject { + @override + late final jni.JObjType $type = type; - /// from: public boolean setReadable(boolean z, boolean z1) - bool setReadable( - bool z, - bool z1, - ) { - return _setReadable(reference, z ? 1 : 0, z1 ? 1 : 0).boolean; - } + ReplayIntegration.fromRef( + jni.JObjectPtr ref, + ) : super.fromRef(ref); - static final _setReadable1 = jniLookup< + /// The type which includes information such as the signature of this class. + static const type = $ReplayIntegrationType(); + static final _new0 = jniLookup< ffi.NativeFunction< jni.JniResult Function( - ffi.Pointer, ffi.Uint8)>>("File__setReadable1") - .asFunction, int)>(); + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>>("ReplayIntegration__new0") + .asFunction< + jni.JniResult Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>(); - /// from: public boolean setReadable(boolean z) - bool setReadable1( - bool z, + /// from: public void (android.content.Context context, io.sentry.transport.ICurrentDateProvider iCurrentDateProvider, kotlin.jvm.functions.Function0 function0, kotlin.jvm.functions.Function1 function1, kotlin.jvm.functions.Function1 function11) + /// The returned object must be released after use, by calling the [release] method. + factory ReplayIntegration( + jni.JObject context, + jni.JObject iCurrentDateProvider, + jni.JObject function0, + jni.JObject function1, + jni.JObject function11, ) { - return _setReadable1(reference, z ? 1 : 0).boolean; + return ReplayIntegration.fromRef(_new0( + context.reference, + iCurrentDateProvider.reference, + function0.reference, + function1.reference, + function11.reference) + .object); } - static final _setExecutable = jniLookup< + static final _new1 = jniLookup< ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, ffi.Uint8, - ffi.Uint8)>>("File__setExecutable") - .asFunction, int, int)>(); + jni.JniResult Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Int32, + ffi.Pointer)>>("ReplayIntegration__new1") + .asFunction< + jni.JniResult Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + int, + ffi.Pointer)>(); - /// from: public boolean setExecutable(boolean z, boolean z1) - bool setExecutable( - bool z, - bool z1, + /// from: public void (android.content.Context context, io.sentry.transport.ICurrentDateProvider iCurrentDateProvider, kotlin.jvm.functions.Function0 function0, kotlin.jvm.functions.Function1 function1, kotlin.jvm.functions.Function1 function11, int i, kotlin.jvm.internal.DefaultConstructorMarker defaultConstructorMarker) + /// The returned object must be released after use, by calling the [release] method. + factory ReplayIntegration.new1( + jni.JObject context, + jni.JObject iCurrentDateProvider, + jni.JObject function0, + jni.JObject function1, + jni.JObject function11, + int i, + jni.JObject defaultConstructorMarker, ) { - return _setExecutable(reference, z ? 1 : 0, z1 ? 1 : 0).boolean; + return ReplayIntegration.fromRef(_new1( + context.reference, + iCurrentDateProvider.reference, + function0.reference, + function1.reference, + function11.reference, + i, + defaultConstructorMarker.reference) + .object); } - static final _setExecutable1 = jniLookup< + static final _new2 = jniLookup< ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Uint8)>>("File__setExecutable1") - .asFunction, int)>(); + jni.JniResult Function(ffi.Pointer, + ffi.Pointer)>>("ReplayIntegration__new2") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); - /// from: public boolean setExecutable(boolean z) - bool setExecutable1( - bool z, + /// from: public void (android.content.Context context, io.sentry.transport.ICurrentDateProvider iCurrentDateProvider) + /// The returned object must be released after use, by calling the [release] method. + factory ReplayIntegration.new2( + jni.JObject context, + jni.JObject iCurrentDateProvider, ) { - return _setExecutable1(reference, z ? 1 : 0).boolean; + return ReplayIntegration.fromRef( + _new2(context.reference, iCurrentDateProvider.reference).object); } - static final _canExecute = jniLookup< + static final _getReplayCacheDir = jniLookup< ffi .NativeFunction)>>( - "File__canExecute") + "ReplayIntegration__getReplayCacheDir") .asFunction)>(); - /// from: public boolean canExecute() - bool canExecute() { - return _canExecute(reference).boolean; + /// from: public final java.io.File getReplayCacheDir() + /// The returned object must be released after use, by calling the [release] method. + File getReplayCacheDir() { + return const $FileType().fromRef(_getReplayCacheDir(reference).object); } - static final _listRoots = - jniLookup>("File__listRoots") - .asFunction(); + static final _register = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>>("ReplayIntegration__register") + .asFunction< + jni.JniResult Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); - /// from: static public java.io.File[] listRoots() - /// The returned object must be released after use, by calling the [release] method. - static jni.JArray listRoots() { - return const jni.JArrayType($FileType()).fromRef(_listRoots().object); + /// from: public void register(io.sentry.IHub iHub, io.sentry.SentryOptions sentryOptions) + void register( + jni.JObject iHub, + jni.JObject sentryOptions, + ) { + return _register(reference, iHub.reference, sentryOptions.reference) + .check(); } - static final _getTotalSpace = jniLookup< + static final _isRecording = jniLookup< ffi .NativeFunction)>>( - "File__getTotalSpace") + "ReplayIntegration__isRecording") .asFunction)>(); - /// from: public long getTotalSpace() - int getTotalSpace() { - return _getTotalSpace(reference).long; + /// from: public boolean isRecording() + bool isRecording() { + return _isRecording(reference).boolean; } - static final _getFreeSpace = jniLookup< + static final _start = jniLookup< ffi .NativeFunction)>>( - "File__getFreeSpace") + "ReplayIntegration__start") .asFunction)>(); - /// from: public long getFreeSpace() - int getFreeSpace() { - return _getFreeSpace(reference).long; + /// from: public void start() + void start() { + return _start(reference).check(); } - static final _getUsableSpace = jniLookup< + static final _resume = jniLookup< ffi .NativeFunction)>>( - "File__getUsableSpace") + "ReplayIntegration__resume") .asFunction)>(); - /// from: public long getUsableSpace() - int getUsableSpace() { - return _getUsableSpace(reference).long; + /// from: public void resume() + void resume() { + return _resume(reference).check(); } - static final _createTempFile = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer)>>("File__createTempFile") + static final _sendReplayForEvent = jniLookup< + ffi.NativeFunction< + jni.JniResult Function(ffi.Pointer, + ffi.Pointer, ffi.Pointer)>>( + "ReplayIntegration__sendReplayForEvent") .asFunction< jni.JniResult Function(ffi.Pointer, ffi.Pointer, ffi.Pointer)>(); - /// from: static public java.io.File createTempFile(java.lang.String string, java.lang.String string1, java.io.File file) - /// The returned object must be released after use, by calling the [release] method. - static File createTempFile( - jni.JString string, - jni.JString string1, - File file, + /// from: public void sendReplayForEvent(io.sentry.SentryEvent sentryEvent, io.sentry.Hint hint) + void sendReplayForEvent( + jni.JObject sentryEvent, + jni.JObject hint, ) { - return const $FileType().fromRef( - _createTempFile(string.reference, string1.reference, file.reference) - .object); + return _sendReplayForEvent(reference, sentryEvent.reference, hint.reference) + .check(); } - static final _createTempFile1 = jniLookup< + static final _sendReplay = jniLookup< ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("File__createTempFile1") + jni.JniResult Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>>("ReplayIntegration__sendReplay") .asFunction< - jni.JniResult Function( + jni.JniResult Function(ffi.Pointer, ffi.Pointer, ffi.Pointer, ffi.Pointer)>(); - /// from: static public java.io.File createTempFile(java.lang.String string, java.lang.String string1) - /// The returned object must be released after use, by calling the [release] method. - static File createTempFile1( + /// from: public void sendReplay(java.lang.Boolean boolean, java.lang.String string, io.sentry.Hint hint) + void sendReplay( + jni.JBoolean boolean, jni.JString string, - jni.JString string1, + jni.JObject hint, ) { - return const $FileType() - .fromRef(_createTempFile1(string.reference, string1.reference).object); + return _sendReplay( + reference, boolean.reference, string.reference, hint.reference) + .check(); } - static final _compareTo = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("File__compareTo") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); + static final _getReplayId = jniLookup< + ffi + .NativeFunction)>>( + "ReplayIntegration__getReplayId") + .asFunction)>(); - /// from: public int compareTo(java.io.File file) - int compareTo( - File file, - ) { - return _compareTo(reference, file.reference).integer; + /// from: public io.sentry.protocol.SentryId getReplayId() + /// The returned object must be released after use, by calling the [release] method. + jni.JObject getReplayId() { + return const jni.JObjectType().fromRef(_getReplayId(reference).object); } - static final _equals1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("File__equals1") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); + static final _pause = jniLookup< + ffi + .NativeFunction)>>( + "ReplayIntegration__pause") + .asFunction)>(); - /// from: public boolean equals(java.lang.Object object) - bool equals1( - jni.JObject object, - ) { - return _equals1(reference, object.reference).boolean; + /// from: public void pause() + void pause() { + return _pause(reference).check(); } - static final _hashCode1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__hashCode1") + static final _stop = jniLookup< + ffi + .NativeFunction)>>( + "ReplayIntegration__stop") .asFunction)>(); - /// from: public int hashCode() - int hashCode1() { - return _hashCode1(reference).integer; + /// from: public void stop() + void stop() { + return _stop(reference).check(); } - static final _toString1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__toString1") - .asFunction)>(); + static final _onScreenshotRecorded = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>>( + "ReplayIntegration__onScreenshotRecorded") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>(); - /// from: public java.lang.String toString() - /// The returned object must be released after use, by calling the [release] method. - jni.JString toString1() { - return const jni.JStringType().fromRef(_toString1(reference).object); + /// from: public void onScreenshotRecorded(android.graphics.Bitmap bitmap) + void onScreenshotRecorded( + jni.JObject bitmap, + ) { + return _onScreenshotRecorded(reference, bitmap.reference).check(); } - static final _toPath = jniLookup< + static final _onScreenshotRecorded1 = jniLookup< ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__toPath") + jni.JniResult Function( + ffi.Pointer, + ffi.Pointer, + ffi.Int64)>>("ReplayIntegration__onScreenshotRecorded1") + .asFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer, int)>(); + + /// from: public void onScreenshotRecorded(java.io.File file, long j) + void onScreenshotRecorded1( + File file, + int j, + ) { + return _onScreenshotRecorded1(reference, file.reference, j).check(); + } + + static final _close = jniLookup< + ffi + .NativeFunction)>>( + "ReplayIntegration__close") .asFunction)>(); - /// from: public java.nio.file.Path toPath() - /// The returned object must be released after use, by calling the [release] method. - jni.JObject toPath() { - return const jni.JObjectType().fromRef(_toPath(reference).object); + /// from: public void close() + void close() { + return _close(reference).check(); } - static final _compareTo1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("File__compareTo1") + static final _onConfigurationChanged = jniLookup< + ffi.NativeFunction< + jni.JniResult Function( + ffi.Pointer, ffi.Pointer)>>( + "ReplayIntegration__onConfigurationChanged") .asFunction< jni.JniResult Function( ffi.Pointer, ffi.Pointer)>(); - /// from: public int compareTo(java.lang.Object object) - int compareTo1( - jni.JObject object, + /// from: public void onConfigurationChanged(android.content.res.Configuration configuration) + void onConfigurationChanged( + jni.JObject configuration, ) { - return _compareTo1(reference, object.reference).integer; + return _onConfigurationChanged(reference, configuration.reference).check(); + } + + static final _onLowMemory = jniLookup< + ffi + .NativeFunction)>>( + "ReplayIntegration__onLowMemory") + .asFunction)>(); + + /// from: public void onLowMemory() + void onLowMemory() { + return _onLowMemory(reference).check(); } } -class $FileType extends jni.JObjType { - const $FileType(); +class $ReplayIntegrationType extends jni.JObjType { + const $ReplayIntegrationType(); @override - String get signature => r"Ljava/io/File;"; + String get signature => r"Lio/sentry/android/replay/ReplayIntegration;"; @override - File fromRef(jni.JObjectPtr ref) => File.fromRef(ref); + ReplayIntegration fromRef(jni.JObjectPtr ref) => + ReplayIntegration.fromRef(ref); @override jni.JObjType get superType => const jni.JObjectType(); @@ -1794,10 +1819,11 @@ class $FileType extends jni.JObjType { final superCount = 1; @override - int get hashCode => ($FileType).hashCode; + int get hashCode => ($ReplayIntegrationType).hashCode; @override bool operator ==(Object other) { - return other.runtimeType == ($FileType) && other is $FileType; + return other.runtimeType == ($ReplayIntegrationType) && + other is $ReplayIntegrationType; } } diff --git a/flutter/lib/src/native/java/sentry_native_java.dart b/flutter/lib/src/native/java/sentry_native_java.dart index 84ae6bee0d..7245a9f34b 100644 --- a/flutter/lib/src/native/java/sentry_native_java.dart +++ b/flutter/lib/src/native/java/sentry_native_java.dart @@ -8,7 +8,10 @@ import 'binding.dart' as java; @internal class SentryNativeJava extends SentryNativeChannel { SentryNativeJava(super.channel) { + // Necessary for the generated binding to work as of jnigen v0.6.0 + // This may change in the future. Jni.initDLApi(); + java.SentryFlutterReplay.recorder = AndroidReplayRecorder.create(); } } diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index fcb27c3eae..55eaa0ce88 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -27,7 +27,7 @@ dependencies: package_info_plus: '>=1.0.0' meta: ^1.3.0 ffi: ^2.0.0 - jni: ^0.6.0 + jni: 0.6.0 path: ^1.8.0 dev_dependencies: @@ -41,7 +41,7 @@ dev_dependencies: remove_from_coverage: ^2.0.0 flutter_localizations: sdk: flutter - jnigen: ^0.6.0 + jnigen: 0.6.0 flutter: plugin: From fb8bbb4d926fde10bb2630c81cb54b44995e9e00 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 26 Apr 2024 16:23:57 +0200 Subject: [PATCH 22/74] move native setup to the native sdk integration --- .../io/sentry/flutter/SentryFlutterReplay.kt | 2 +- .../integrations/native_sdk_integration.dart | 8 +++ flutter/lib/src/native/factory_real.dart | 5 +- .../native/java/android_replay_recorder.dart | 10 +++- .../src/native/java/sentry_native_java.dart | 13 +++-- flutter/lib/src/replay/recorder.dart | 56 ++++++++++++++----- flutter/lib/src/sentry_flutter.dart | 6 -- 7 files changed, 70 insertions(+), 30 deletions(-) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt index c46386ffaa..c67840fae7 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt @@ -5,7 +5,7 @@ import io.sentry.android.replay.ReplayIntegration object SentryFlutterReplay { // Set by the Flutter side, read during SentryAndroid.init() - lateinit var recorder: Recorder + var recorder: Recorder? = null // Set by SentryAndroid.init(), read by the Flutter side in recorder.start() lateinit var integration: ReplayIntegration diff --git a/flutter/lib/src/integrations/native_sdk_integration.dart b/flutter/lib/src/integrations/native_sdk_integration.dart index fe48c5b02e..a5e26ac3f0 100644 --- a/flutter/lib/src/integrations/native_sdk_integration.dart +++ b/flutter/lib/src/integrations/native_sdk_integration.dart @@ -2,6 +2,9 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:sentry/sentry.dart'; +import '../native/factory.dart'; +import '../native/sentry_native.dart'; +import '../sentry_flutter.dart'; import '../sentry_flutter_options.dart'; /// Enables Sentry's native SDKs (Android and iOS) with options. @@ -14,9 +17,14 @@ class NativeSdkIntegration implements Integration { @override Future call(Hub hub, SentryFlutterOptions options) async { _options = options; + if (!options.autoInitializeNativeSdk) { return; } + + final binding = createBinding(options.platformChecker, _channel, options); + SentryFlutter.native = SentryNative(options, binding); + try { await _channel.invokeMethod('initNativeSdk', { 'dsn': options.dsn, diff --git a/flutter/lib/src/native/factory_real.dart b/flutter/lib/src/native/factory_real.dart index d5ee4f5cca..80e6005eee 100644 --- a/flutter/lib/src/native/factory_real.dart +++ b/flutter/lib/src/native/factory_real.dart @@ -6,11 +6,12 @@ import 'java/sentry_native_java.dart'; import 'sentry_native_binding.dart'; import 'sentry_native_channel.dart'; -SentryNativeBinding createBinding(PlatformChecker pc, MethodChannel channel) { +SentryNativeBinding createBinding( + PlatformChecker pc, MethodChannel channel, SentryFlutterOptions options) { if (pc.platform.isIOS || pc.platform.isMacOS) { return SentryNativeCocoa(channel); } else if (pc.platform.isAndroid) { - return SentryNativeJava(channel); + return SentryNativeJava(channel, options); } else { return SentryNativeChannel(channel); } diff --git a/flutter/lib/src/native/java/android_replay_recorder.dart b/flutter/lib/src/native/java/android_replay_recorder.dart index 39f21c4e41..b045ecc27f 100644 --- a/flutter/lib/src/native/java/android_replay_recorder.dart +++ b/flutter/lib/src/native/java/android_replay_recorder.dart @@ -7,17 +7,19 @@ import 'package:path/path.dart' as path; import '../../replay/recorder.dart'; import '../../replay/recorder_config.dart'; +import '../../sentry_flutter_options.dart'; import 'binding.dart' as java; @internal class AndroidReplayRecorder implements java.$RecorderImpl { + final SentryFlutterOptions _options; late ScreenshotRecorder _recorder; late java.ReplayIntegration? _integration; - AndroidReplayRecorder._(); + AndroidReplayRecorder._(this._options); - static java.Recorder create() => - java.Recorder.implement(AndroidReplayRecorder._()); + static java.Recorder create(SentryFlutterOptions options) => + java.Recorder.implement(AndroidReplayRecorder._(options)); @override void pause() => _recorder.stop(); @@ -59,7 +61,9 @@ class AndroidReplayRecorder implements java.$RecorderImpl { config.getFrameRate(), ), callback, + _options.logger, ); + _recorder.start(); } diff --git a/flutter/lib/src/native/java/sentry_native_java.dart b/flutter/lib/src/native/java/sentry_native_java.dart index 7245a9f34b..b7ec1223b7 100644 --- a/flutter/lib/src/native/java/sentry_native_java.dart +++ b/flutter/lib/src/native/java/sentry_native_java.dart @@ -1,17 +1,20 @@ import 'package:jni/jni.dart'; import 'package:meta/meta.dart'; +import '../../sentry_flutter_options.dart'; import '../sentry_native_channel.dart'; import 'android_replay_recorder.dart'; import 'binding.dart' as java; @internal class SentryNativeJava extends SentryNativeChannel { - SentryNativeJava(super.channel) { - // Necessary for the generated binding to work as of jnigen v0.6.0 - // This may change in the future. - Jni.initDLApi(); + SentryNativeJava(super.channel, SentryFlutterOptions options) { + if (options.replay.isEnabled) { + // Necessary for the generated binding to work as of jnigen v0.6.0 + // This may change in the future. + Jni.initDLApi(); - java.SentryFlutterReplay.recorder = AndroidReplayRecorder.create(); + java.SentryFlutterReplay.recorder = AndroidReplayRecorder.create(options); + } } } diff --git a/flutter/lib/src/replay/recorder.dart b/flutter/lib/src/replay/recorder.dart index 976357cb98..5437acbbab 100644 --- a/flutter/lib/src/replay/recorder.dart +++ b/flutter/lib/src/replay/recorder.dart @@ -4,7 +4,7 @@ import 'dart:ui'; import 'package:flutter/rendering.dart'; import 'package:meta/meta.dart'; -import '../screenshot/sentry_screenshot_widget.dart'; +import '../../sentry_flutter.dart'; import 'recorder_config.dart'; import 'recorder_widget_filter.dart'; import 'scheduler.dart'; @@ -16,41 +16,63 @@ typedef ScreenshotRecorderCallback = Future Function(Image); class ScreenshotRecorder { final ScreenshotRecorderConfig _config; final ScreenshotRecorderCallback _callback; + final SentryLogger _logger; late final Scheduler _scheduler; - final Paint _widgetObscurePaint = Paint()..color = const Color(0x7f000000); + final Paint _widgetObscurePaint = Paint() + ..color = Color.fromARGB(255, 0, 0, 0); + bool warningLogged = false; - ScreenshotRecorder(this._config, this._callback) { + ScreenshotRecorder(this._config, this._callback, this._logger) { final frameDuration = Duration(milliseconds: 1000 ~/ _config.frameRate); _scheduler = Scheduler(frameDuration, _capture); } - void start() => _scheduler.start(); + void start() { + _logger(SentryLevel.debug, "Replay: starting replay capture."); + _scheduler.start(); + } - void stop() => _scheduler.stop(); + void stop() { + _scheduler.stop(); + _logger(SentryLevel.debug, "Replay: replay capture stopped."); + } // TODO try-catch Future _capture(Duration sinceSchedulerEpoch) async { final context = sentryScreenshotWidgetGlobalKey.currentContext; - final renderObject = context?.findRenderObject(); + final renderObject = context?.findRenderObject() as RenderRepaintBoundary?; + + if (context == null || renderObject == null) { + if (!warningLogged) { + _logger( + SentryLevel.warning, + "Replay: SentryScreenshotWidget is not attached. " + "Skipping replay capture."); + warningLogged = true; + } + return; + } - if (context != null && renderObject is RenderRepaintBoundary) { + try { + // TODO remove these final watch = Stopwatch()..start(); + final watch2 = Stopwatch()..start(); - // TODO downsize right away to the desired resolution: use _config final width = renderObject.size.width.round(); final height = renderObject.size.height.round(); final pixelRatio = 1.0; - final recorder = PictureRecorder(); - final canvas = Canvas(recorder); - + // Note: we capture the image first and visit children synchronously on the main UI loop. final futureImage = renderObject.toImage(pixelRatio: pixelRatio); - watch.printAndReset("renderObject.toImage()"); + watch.printAndReset("renderObject.toImage($pixelRatio)"); final filter = WidgetFilter(pixelRatio); context.visitChildElements(filter.obscure); watch.printAndReset("collect widget boundaries"); + // Then we draw the image and obscure collected coordinates asynchronously. + final recorder = PictureRecorder(); + final canvas = Canvas(recorder); final image = await futureImage; watch.printAndReset("await image"); try { @@ -78,6 +100,13 @@ class ScreenshotRecorder { } finally { picture.dispose(); } + + _logger(SentryLevel.debug, + "Replay: captured a screenshot in ${watch2.elapsedMilliseconds} ms."); + watch2.printAndReset("complete capture"); + } catch (e, stackTrace) { + _logger(SentryLevel.error, "Replay: failed to capture screenshot.", + exception: e, stackTrace: stackTrace); } } @@ -90,7 +119,8 @@ class ScreenshotRecorder { extension _WatchPrinter on Stopwatch { void printAndReset(String message) { - print("$message: $elapsedMicroseconds us"); + print( + "RECORDER | $message: ${(elapsedMicroseconds / 1000).toStringAsFixed(3)} ms"); reset(); } } diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index 1931e86981..9173b20e92 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -12,7 +12,6 @@ import 'event_processor/widget_event_processor.dart'; import 'frame_callback_handler.dart'; import 'integrations/connectivity/connectivity_integration.dart'; import 'integrations/screenshot_integration.dart'; -import 'native/factory.dart'; import 'native/native_scope_observer.dart'; import 'profiling.dart'; import 'renderer/renderer.dart'; @@ -50,11 +49,6 @@ mixin SentryFlutter { flutterOptions.rendererWrapper = rendererWrapper; } - if (flutterOptions.platformChecker.hasNativeIntegration) { - final binding = createBinding(flutterOptions.platformChecker, channel); - _native = SentryNative(flutterOptions, binding); - } - final platformDispatcher = PlatformDispatcher.instance; final wrapper = PlatformDispatcherWrapper(platformDispatcher); From 7c8fd42d0ba78e3b488dae3ea96f28580c8fd25b Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 29 Apr 2024 14:06:24 +0200 Subject: [PATCH 23/74] cleanup & improvements --- flutter/android/proguard-rules.pro | 2 +- .../src/main/cpp/sentry_android_binding.c | 34 ------------------- .../io/sentry/flutter/SentryFlutterPlugin.kt | 2 +- .../io/sentry/flutter/SentryFlutterReplay.kt | 3 +- .../native/java/android_replay_recorder.dart | 8 +++++ flutter/lib/src/native/java/binding.dart | 27 --------------- flutter/lib/src/replay/recorder.dart | 24 +++++++++---- 7 files changed, 29 insertions(+), 71 deletions(-) diff --git a/flutter/android/proguard-rules.pro b/flutter/android/proguard-rules.pro index ff81e3db54..a82cd7fac6 100644 --- a/flutter/android/proguard-rules.pro +++ b/flutter/android/proguard-rules.pro @@ -1,10 +1,10 @@ -keep class io.sentry.flutter.** { *; } # JNI generated binding code (keep up to date with ffi-jni.yaml). +-keep class java.io.File -keep class io.sentry.android.replay.Recorder -keep class io.sentry.android.replay.ScreenshotRecorderConfig -keep class io.sentry.android.replay.ReplayIntegration --keep class java.io.File # To ensure that stack traces is unambiguous # https://developer.android.com/studio/build/shrink-code#decode-stack-trace diff --git a/flutter/android/src/main/cpp/sentry_android_binding.c b/flutter/android/src/main/cpp/sentry_android_binding.c index a2b17ab679..f610456970 100644 --- a/flutter/android/src/main/cpp/sentry_android_binding.c +++ b/flutter/android/src/main/cpp/sentry_android_binding.c @@ -908,40 +908,6 @@ JniResult get_File__separatorChar() { // io.sentry.flutter.SentryFlutterReplay jclass _c_SentryFlutterReplay = NULL; -jmethodID _m_SentryFlutterReplay__getRecorder = NULL; -FFI_PLUGIN_EXPORT -JniResult SentryFlutterReplay__getRecorder(jobject self_) { - load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__getRecorder, - "getRecorder", "()Lio/sentry/android/replay/Recorder;"); - if (_m_SentryFlutterReplay__getRecorder == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_SentryFlutterReplay__getRecorder); - return to_global_ref_result(_result); -} - -jmethodID _m_SentryFlutterReplay__setRecorder = NULL; -FFI_PLUGIN_EXPORT -JniResult SentryFlutterReplay__setRecorder(jobject self_, jobject recorder) { - load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__setRecorder, - "setRecorder", "(Lio/sentry/android/replay/Recorder;)V"); - if (_m_SentryFlutterReplay__setRecorder == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_SentryFlutterReplay__setRecorder, - recorder); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - jmethodID _m_SentryFlutterReplay__getIntegration = NULL; FFI_PLUGIN_EXPORT JniResult SentryFlutterReplay__getIntegration(jobject self_) { diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index f6df64890d..7f86f00ff9 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -129,7 +129,7 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { context, dateProvider = CurrentDateProvider.getInstance(), recorderProvider = { recorder }, - recorderConfigProvider = null, // TODO implement in dart + recorderConfigProvider = null, replayCacheProvider = null) options.addIntegration(replay) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt index c67840fae7..7266d852e4 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt @@ -4,7 +4,8 @@ import io.sentry.android.replay.Recorder import io.sentry.android.replay.ReplayIntegration object SentryFlutterReplay { - // Set by the Flutter side, read during SentryAndroid.init() + // Set by the Flutter side, read during SentryAndroid.init(). If null, replay is disabled. + @JvmField var recorder: Recorder? = null // Set by SentryAndroid.init(), read by the Flutter side in recorder.start() diff --git a/flutter/lib/src/native/java/android_replay_recorder.dart b/flutter/lib/src/native/java/android_replay_recorder.dart index b045ecc27f..7dfda624a4 100644 --- a/flutter/lib/src/native/java/android_replay_recorder.dart +++ b/flutter/lib/src/native/java/android_replay_recorder.dart @@ -36,6 +36,14 @@ class AndroidReplayRecorder implements java.$RecorderImpl { jniCacheDir.getAbsolutePath().toDartString(releaseOriginal: true); jniCacheDir.release(); + // Note: time measurements using a Stopwatch in a debug build: + // save as rawRgba (1230876 bytes): 0.257 ms -- discarded + // save as PNG (25401 bytes): 43.110 ms -- used for the final image + // image size: 25401 bytes + // save to file: 3.677 ms + // new jfile: 0.400 ms + // onScreenshotRecorded1: 1.237 ms + // released and exiting callback: 0.021 ms ScreenshotRecorderCallback callback = (image) async { var imageData = await image.toByteData(format: ImageByteFormat.png); if (imageData != null) { diff --git a/flutter/lib/src/native/java/binding.dart b/flutter/lib/src/native/java/binding.dart index af656d57a2..74c4979d8f 100644 --- a/flutter/lib/src/native/java/binding.dart +++ b/flutter/lib/src/native/java/binding.dart @@ -845,33 +845,6 @@ class SentryFlutterReplay extends jni.JObject { static set integration(ReplayIntegration value) => _set_integration(value.reference).check(); - static final _getRecorder = jniLookup< - ffi - .NativeFunction)>>( - "SentryFlutterReplay__getRecorder") - .asFunction)>(); - - /// from: public final io.sentry.android.replay.Recorder getRecorder() - /// The returned object must be released after use, by calling the [release] method. - Recorder getRecorder() { - return const $RecorderType().fromRef(_getRecorder(reference).object); - } - - static final _setRecorder = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("SentryFlutterReplay__setRecorder") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public final void setRecorder(io.sentry.android.replay.Recorder recorder) - void setRecorder( - Recorder recorder, - ) { - return _setRecorder(reference, recorder.reference).check(); - } - static final _getIntegration = jniLookup< ffi .NativeFunction)>>( diff --git a/flutter/lib/src/replay/recorder.dart b/flutter/lib/src/replay/recorder.dart index 5437acbbab..bcaf6d39f1 100644 --- a/flutter/lib/src/replay/recorder.dart +++ b/flutter/lib/src/replay/recorder.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:math'; import 'dart:ui'; import 'package:flutter/rendering.dart'; @@ -58,9 +59,14 @@ class ScreenshotRecorder { final watch = Stopwatch()..start(); final watch2 = Stopwatch()..start(); - final width = renderObject.size.width.round(); - final height = renderObject.size.height.round(); - final pixelRatio = 1.0; + // We may scale here already if the desired resolution is lower than the actual one. + // On the other hand, if it's higher, we scale up in picture.toImage(). + final srcWidth = renderObject.size.width.round(); + final srcHeight = renderObject.size.height.round(); + final pixelRatioX = _config.width / srcWidth; + final pixelRatioY = _config.height / srcHeight; + final outputPixelRatio = min(pixelRatioY, pixelRatioX); + final pixelRatio = min(1.0, outputPixelRatio); // Note: we capture the image first and visit children synchronously on the main UI loop. final futureImage = renderObject.toImage(pixelRatio: pixelRatio); @@ -69,12 +75,13 @@ class ScreenshotRecorder { final filter = WidgetFilter(pixelRatio); context.visitChildElements(filter.obscure); watch.printAndReset("collect widget boundaries"); + final blockingTime = watch2.elapsedMilliseconds; // Then we draw the image and obscure collected coordinates asynchronously. final recorder = PictureRecorder(); final canvas = Canvas(recorder); final image = await futureImage; - watch.printAndReset("await image"); + watch.printAndReset("await image (${image.width}x${image.height})"); try { canvas.drawImage(image, Offset.zero, Paint()); watch.printAndReset("drawImage()"); @@ -89,8 +96,11 @@ class ScreenshotRecorder { watch.printAndReset("endRecording()"); try { - final finalImage = await picture.toImage(width, height); - watch.printAndReset("picture.toImage()"); + final finalImage = await picture.toImage( + (srcWidth * outputPixelRatio).round(), + (srcHeight * outputPixelRatio).round()); + watch.printAndReset( + "picture.toImage(${finalImage.width}x${finalImage.height})"); try { await _callback(finalImage); watch.printAndReset("callback()"); @@ -102,7 +112,7 @@ class ScreenshotRecorder { } _logger(SentryLevel.debug, - "Replay: captured a screenshot in ${watch2.elapsedMilliseconds} ms."); + "Replay: captured a screenshot in ${watch2.elapsedMilliseconds} ms ($blockingTime ms blocking)."); watch2.printAndReset("complete capture"); } catch (e, stackTrace) { _logger(SentryLevel.error, "Replay: failed to capture screenshot.", From 0570f354e4557123f265b7d8fede3276f95bb2ad Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 29 Apr 2024 21:39:19 +0200 Subject: [PATCH 24/74] improve widget filter and implement redact options --- .../native/java/android_replay_recorder.dart | 1 + flutter/lib/src/replay/recorder.dart | 50 +++++--- .../src/replay/recorder_widget_filter.dart | 118 +++++++++++++----- flutter/lib/src/sentry_replay_options.dart | 22 ++-- 4 files changed, 133 insertions(+), 58 deletions(-) diff --git a/flutter/lib/src/native/java/android_replay_recorder.dart b/flutter/lib/src/native/java/android_replay_recorder.dart index 7dfda624a4..9677efd9fa 100644 --- a/flutter/lib/src/native/java/android_replay_recorder.dart +++ b/flutter/lib/src/native/java/android_replay_recorder.dart @@ -70,6 +70,7 @@ class AndroidReplayRecorder implements java.$RecorderImpl { ), callback, _options.logger, + _options.replay, ); _recorder.start(); diff --git a/flutter/lib/src/replay/recorder.dart b/flutter/lib/src/replay/recorder.dart index bcaf6d39f1..a15b78a4b2 100644 --- a/flutter/lib/src/replay/recorder.dart +++ b/flutter/lib/src/replay/recorder.dart @@ -6,6 +6,7 @@ import 'package:flutter/rendering.dart'; import 'package:meta/meta.dart'; import '../../sentry_flutter.dart'; +import '../sentry_replay_options.dart'; import 'recorder_config.dart'; import 'recorder_widget_filter.dart'; import 'scheduler.dart'; @@ -13,19 +14,28 @@ import 'scheduler.dart'; @internal typedef ScreenshotRecorderCallback = Future Function(Image); +// TODO evaluate [notifications](https://api.flutter.dev/flutter/widgets/Notification-class.html) +// to only collect screenshots when there are changes. +// We probably can't use build() because inner repaintboundaries won't propagate up? @internal class ScreenshotRecorder { final ScreenshotRecorderConfig _config; final ScreenshotRecorderCallback _callback; final SentryLogger _logger; + final SentryReplayOptions _options; + WidgetFilter? _widgetFilter; late final Scheduler _scheduler; - final Paint _widgetObscurePaint = Paint() - ..color = Color.fromARGB(255, 0, 0, 0); bool warningLogged = false; - ScreenshotRecorder(this._config, this._callback, this._logger) { + ScreenshotRecorder( + this._config, this._callback, this._logger, this._options) { final frameDuration = Duration(milliseconds: 1000 ~/ _config.frameRate); _scheduler = Scheduler(frameDuration, _capture); + if (_options.redactAllText || _options.redactAllImages) { + _widgetFilter = WidgetFilter( + redactText: _options.redactAllText, + redactImages: _options.redactAllImages); + } } void start() { @@ -38,7 +48,6 @@ class ScreenshotRecorder { _logger(SentryLevel.debug, "Replay: replay capture stopped."); } - // TODO try-catch Future _capture(Duration sinceSchedulerEpoch) async { final context = sentryScreenshotWidgetGlobalKey.currentContext; final renderObject = context?.findRenderObject() as RenderRepaintBoundary?; @@ -61,20 +70,27 @@ class ScreenshotRecorder { // We may scale here already if the desired resolution is lower than the actual one. // On the other hand, if it's higher, we scale up in picture.toImage(). - final srcWidth = renderObject.size.width.round(); - final srcHeight = renderObject.size.height.round(); + final srcWidth = renderObject.size.width; + final srcHeight = renderObject.size.height; final pixelRatioX = _config.width / srcWidth; final pixelRatioY = _config.height / srcHeight; final outputPixelRatio = min(pixelRatioY, pixelRatioX); final pixelRatio = min(1.0, outputPixelRatio); - // Note: we capture the image first and visit children synchronously on the main UI loop. + // First, we synchronously capture the image and enumarete widgets on the main UI loop. final futureImage = renderObject.toImage(pixelRatio: pixelRatio); watch.printAndReset("renderObject.toImage($pixelRatio)"); - final filter = WidgetFilter(pixelRatio); - context.visitChildElements(filter.obscure); - watch.printAndReset("collect widget boundaries"); + final filter = _widgetFilter; + if (filter != null) { + filter.setupAndClear( + pixelRatio, + Rect.fromLTWH(0, 0, srcWidth * pixelRatio, srcHeight * pixelRatio), + ); + context.visitChildElements(filter.obscure); + watch.printAndReset("collect widget boundaries"); + } + final blockingTime = watch2.elapsedMilliseconds; // Then we draw the image and obscure collected coordinates asynchronously. @@ -89,8 +105,10 @@ class ScreenshotRecorder { image.dispose(); } - _obscureWidgets(canvas, filter.bounds); - watch.printAndReset("obscureWidgets()"); + if (filter != null) { + _obscureWidgets(canvas, filter.items); + watch.printAndReset("obscureWidgets(${filter.items.length} items)"); + } final picture = recorder.endRecording(); watch.printAndReset("endRecording()"); @@ -120,9 +138,11 @@ class ScreenshotRecorder { } } - void _obscureWidgets(Canvas canvas, List widgetBounds) { - for (var bounds in widgetBounds) { - canvas.drawRect(bounds, _widgetObscurePaint); + void _obscureWidgets(Canvas canvas, List items) { + final paint = Paint()..style = PaintingStyle.fill; + for (var item in items) { + paint.color = item.color; + canvas.drawRect(item.bounds, paint); } } } diff --git a/flutter/lib/src/replay/recorder_widget_filter.dart b/flutter/lib/src/replay/recorder_widget_filter.dart index da1ebee2c6..75663da578 100644 --- a/flutter/lib/src/replay/recorder_widget_filter.dart +++ b/flutter/lib/src/replay/recorder_widget_filter.dart @@ -3,45 +3,103 @@ import 'package:meta/meta.dart'; @internal class WidgetFilter { - final double _pixelRatio; - final List bounds = []; + static const _defaultColor = Color.fromARGB(255, 0, 0, 0); + final bool redactText; + final bool redactImages; + late double _pixelRatio; + late Rect _bounds; + final List items = []; - WidgetFilter(this._pixelRatio); + WidgetFilter({required this.redactText, required this.redactImages}); + + void setupAndClear(double pixelRatio, Rect bounds) { + print("WidgetFilter setupAndClear()"); + _pixelRatio = pixelRatio; + _bounds = bounds; + items.clear(); + } void obscure(Element element) { final widget = element.widget; - if (widget is Visibility && !widget.visible) { + if (!_isVisible(widget)) { + print("WidgetFilter skipping invisible: $widget"); return; } - if (widget is Opacity && widget.opacity <= 0.0) { - return; + + final obscured = _obscureIfNeeded(element, widget); + if (!obscured) { + element.visitChildElements(obscure); + } + } + + @pragma('vm:prefer-inline') + bool _obscureIfNeeded(Element element, Widget widget) { + Color? color; + + if (redactText && widget is Text) { + color = widget.style?.color; + } else if (redactText && widget is EditableText) { + color = widget.style.color; + } else if (redactImages && widget is Image) { + color = widget.color; + } else { + // No other type is currently obscured. + return false; + } + + final renderObject = element.renderObject; + if (renderObject is! RenderBox) { + print( + "WidgetFilter cannot obscure widget $widget, it's renderObject is not a RenderBox"); + return false; + } + + final size = element.size; + if (size == null) { + print( + "WidgetFilter cannot obscure widget $widget, it's renderObject has a null size"); + return false; } - if (_shouldObscure(widget)) { - final renderObject = element.renderObject; - if (renderObject is RenderBox) { - final offset = renderObject.localToGlobal(Offset.zero); - final size = element.size; - if (size != null) { - bounds.add(Rect.fromLTWH( - offset.dx * _pixelRatio, - offset.dy * _pixelRatio, - size.width * _pixelRatio, - size.height * _pixelRatio, - )); - return; - } - } else { - // TODO fix logging - print( - "Cannot obscure widget $widget, it's renderObject is not a RenderBox"); - } - } - - element.visitChildElements(obscure); + final offset = renderObject.localToGlobal(Offset.zero); + + final rect = Rect.fromLTWH( + offset.dx * _pixelRatio, + offset.dy * _pixelRatio, + size.width * _pixelRatio, + size.height * _pixelRatio, + ); + + if (!rect.overlaps(_bounds)) { + print("WidgetFilter skipping offscreen: $widget"); + return false; + } + + items.add(WidgetFilterItem(color ?? _defaultColor, rect)); + print("WidgetFilter obscuring: $widget"); + + return true; + } + + // We cut off some widgets early because they're not visible at all. + bool _isVisible(Widget widget) { + if (widget is Visibility) { + return widget.visible; + } + if (widget is Opacity) { + return widget.opacity > 0; + } + if (widget is Offstage) { + return !widget.offstage; + } + return true; } +} + +class WidgetFilterItem { + final Color color; + final Rect bounds; - bool _shouldObscure(Widget widget) => - widget is Image || widget is RichText || widget is TextBox; + const WidgetFilterItem(this.color, this.bounds); } diff --git a/flutter/lib/src/sentry_replay_options.dart b/flutter/lib/src/sentry_replay_options.dart index 0035be40a5..838d053ce2 100644 --- a/flutter/lib/src/sentry_replay_options.dart +++ b/flutter/lib/src/sentry_replay_options.dart @@ -32,19 +32,15 @@ class SentryReplayOptions { _errorSampleRate = value; } - // TODO implement in flutter - // /// Redact all text content. Draws a rectangle of text bounds with text color on top. By default - // /// only views extending TextView are redacted. - // /// Default is enabled. - // bool redactAllText = true; - - // TODO implement in flutter - // /// Redact all image content. Draws a rectangle of image bounds with image's dominant color on top. - // /// By default only views extending ImageView with BitmapDrawable or custom Drawable type are - // /// redacted. ColorDrawable, InsetDrawable, VectorDrawable are all considered non-PII, as they come - // /// from the apk. - // /// Default is enabled. - // bool redactAllImages = true; + /// Redact all text content. Draws a rectangle of text bounds with text color + /// on top. Currently, only [Text] and [EditableText] Widgets are redacted. + /// Default is enabled. + var redactAllText = true; + + /// Redact all image content. Draws a rectangle of image bounds with image's + /// dominant color on top. Currently, only [Image] widgets are redacted. + /// Default is enabled. + var redactAllImages = true; @internal bool get isEnabled => From 5c08b21cf10e9f846aa02ea42001834c176b6ced Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 29 Apr 2024 21:57:01 +0200 Subject: [PATCH 25/74] fix image scaling --- flutter/lib/src/replay/recorder.dart | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/flutter/lib/src/replay/recorder.dart b/flutter/lib/src/replay/recorder.dart index a15b78a4b2..801a145782 100644 --- a/flutter/lib/src/replay/recorder.dart +++ b/flutter/lib/src/replay/recorder.dart @@ -68,14 +68,13 @@ class ScreenshotRecorder { final watch = Stopwatch()..start(); final watch2 = Stopwatch()..start(); - // We may scale here already if the desired resolution is lower than the actual one. - // On the other hand, if it's higher, we scale up in picture.toImage(). + // The desired resolution (coming from the configuration) is usually + // rounded to next multitude of 16. Therefore, we scale the image. final srcWidth = renderObject.size.width; final srcHeight = renderObject.size.height; final pixelRatioX = _config.width / srcWidth; final pixelRatioY = _config.height / srcHeight; - final outputPixelRatio = min(pixelRatioY, pixelRatioX); - final pixelRatio = min(1.0, outputPixelRatio); + final pixelRatio = min(pixelRatioY, pixelRatioX); // First, we synchronously capture the image and enumarete widgets on the main UI loop. final futureImage = renderObject.toImage(pixelRatio: pixelRatio); @@ -115,8 +114,7 @@ class ScreenshotRecorder { try { final finalImage = await picture.toImage( - (srcWidth * outputPixelRatio).round(), - (srcHeight * outputPixelRatio).round()); + (srcWidth * pixelRatio).round(), (srcHeight * pixelRatio).round()); watch.printAndReset( "picture.toImage(${finalImage.width}x${finalImage.height})"); try { From 0c55dc60e38e73bb783b822352df0dcb62e82dfd Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 2 May 2024 13:59:05 +0200 Subject: [PATCH 26/74] ktlint format --- .../kotlin/io/sentry/flutter/SentryFlutter.kt | 17 ++- .../io/sentry/flutter/SentryFlutterPlugin.kt | 135 ++++++++++++------ .../io/sentry/flutter/SentryFlutterTest.kt | 58 ++++---- 3 files changed, 131 insertions(+), 79 deletions(-) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutter.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutter.kt index 4aff29fc9b..fb17c3af0f 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutter.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutter.kt @@ -8,14 +8,14 @@ import io.sentry.protocol.SdkVersion import java.util.Locale class SentryFlutter( - private val androidSdk: String, - private val nativeSdk: String, + private val androidSdk: String, + private val nativeSdk: String, ) { var autoPerformanceTracingEnabled = false fun updateOptions( - options: SentryAndroidOptions, - data: Map, + options: SentryAndroidOptions, + data: Map, ) { data.getIfNotNull("dsn") { options.dsn = it @@ -126,7 +126,10 @@ class SentryFlutter( } } - fun updateReplayOptions(options: SentryReplayOptions, data: Map) { + fun updateReplayOptions( + options: SentryReplayOptions, + data: Map, + ) { options.sessionSampleRate = data["sessionSampleRate"] as? Double options.errorSampleRate = data["errorSampleRate"] as? Double } @@ -135,8 +138,8 @@ class SentryFlutter( // Call the `completion` closure if cast to map value with `key` and type `T` is successful. @Suppress("UNCHECKED_CAST") private fun Map.getIfNotNull( - key: String, - callback: (T) -> Unit, + key: String, + callback: (T) -> Unit, ) { (get(key) as? T)?.let { callback(it) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index 7f86f00ff9..8770d7cc3a 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -45,13 +45,16 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { channel.setMethodCallHandler(this) sentryFlutter = - SentryFlutter( - androidSdk = androidSdk, - nativeSdk = nativeSdk, - ) + SentryFlutter( + androidSdk = androidSdk, + nativeSdk = nativeSdk, + ) } - override fun onMethodCall(call: MethodCall, result: Result) { + override fun onMethodCall( + call: MethodCall, + result: Result, + ) { when (call.method) { "initNativeSdk" -> initNativeSdk(call, result) "captureEnvelope" -> captureEnvelope(call, result) @@ -99,7 +102,10 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { // Stub } - private fun initNativeSdk(call: MethodCall, result: Result) { + private fun initNativeSdk( + call: MethodCall, + result: Result, + ) { if (!this::context.isInitialized) { result.error("1", "Context is null", null) return @@ -125,12 +131,13 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { val recorder = SentryFlutterReplay.recorder if (recorder != null && options.cacheDirPath != null) { val replay = - ReplayIntegration( - context, - dateProvider = CurrentDateProvider.getInstance(), - recorderProvider = { recorder }, - recorderConfigProvider = null, - replayCacheProvider = null) + ReplayIntegration( + context, + dateProvider = CurrentDateProvider.getInstance(), + recorderProvider = { recorder }, + recorderConfigProvider = null, + replayCacheProvider = null, + ) options.addIntegration(replay) options.setReplayController(replay) @@ -151,17 +158,18 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { val appStartTime = AppStartMetrics.getInstance().appStartTimeSpan.startTimestamp val isColdStart = - AppStartMetrics.getInstance().appStartType == AppStartMetrics.AppStartType.COLD + AppStartMetrics.getInstance().appStartType == AppStartMetrics.AppStartType.COLD if (appStartTime == null) { Log.w("Sentry", "App start won't be sent due to missing appStartTime") result.success(null) } else { val appStartTimeMillis = DateUtils.nanosToMillis(appStartTime.nanoTimestamp().toDouble()) - val item = mapOf( - "appStartTime" to appStartTimeMillis, - "isColdStart" to isColdStart, - ) + val item = + mapOf( + "appStartTime" to appStartTimeMillis, + "isColdStart" to isColdStart, + ) result.success(item) } } @@ -178,7 +186,10 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { result.success(null) } - private fun endNativeFrames(id: String?, result: Result) { + private fun endNativeFrames( + id: String?, + result: Result, + ) { val activity = activity?.get() if (!sentryFlutter.autoPerformanceTracingEnabled || activity == null || id == null) { if (id == null) { @@ -198,16 +209,21 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { if (total == 0 && slow == 0 && frozen == 0) { result.success(null) } else { - val frames = mapOf( - "totalFrames" to total, - "slowFrames" to slow, - "frozenFrames" to frozen, - ) + val frames = + mapOf( + "totalFrames" to total, + "slowFrames" to slow, + "frozenFrames" to frozen, + ) result.success(frames) } } - private fun setContexts(key: String?, value: Any?, result: Result) { + private fun setContexts( + key: String?, + value: Any?, + result: Result, + ) { if (key == null || value == null) { result.success("") return @@ -219,7 +235,10 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } } - private fun removeContexts(key: String?, result: Result) { + private fun removeContexts( + key: String?, + result: Result, + ) { if (key == null) { result.success("") return @@ -231,7 +250,10 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } } - private fun setUser(user: Map?, result: Result) { + private fun setUser( + user: Map?, + result: Result, + ) { if (user != null) { val options = HubAdapter.getInstance().options val userInstance = User.fromMap(user, options) @@ -242,7 +264,10 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { result.success("") } - private fun addBreadcrumb(breadcrumb: Map?, result: Result) { + private fun addBreadcrumb( + breadcrumb: Map?, + result: Result, + ) { if (breadcrumb != null) { val options = HubAdapter.getInstance().options val breadcrumbInstance = Breadcrumb.fromMap(breadcrumb, options) @@ -257,7 +282,11 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { result.success("") } - private fun setExtra(key: String?, value: String?, result: Result) { + private fun setExtra( + key: String?, + value: String?, + result: Result, + ) { if (key == null || value == null) { result.success("") return @@ -267,7 +296,10 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { result.success("") } - private fun removeExtra(key: String?, result: Result) { + private fun removeExtra( + key: String?, + result: Result, + ) { if (key == null) { result.success("") return @@ -277,7 +309,11 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { result.success("") } - private fun setTag(key: String?, value: String?, result: Result) { + private fun setTag( + key: String?, + value: String?, + result: Result, + ) { if (key == null || value == null) { result.success("") return @@ -287,7 +323,10 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { result.success("") } - private fun removeTag(key: String?, result: Result) { + private fun removeTag( + key: String?, + result: Result, + ) { if (key == null) { result.success("") return @@ -297,7 +336,10 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { result.success("") } - private fun captureEnvelope(call: MethodCall, result: Result) { + private fun captureEnvelope( + call: MethodCall, + result: Result, + ) { if (!Sentry.isEnabled()) { result.error("1", "The Sentry Android SDK is disabled", null) return @@ -352,9 +394,12 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } private class BeforeSendCallbackImpl( - private val sdkVersion: SdkVersion?, + private val sdkVersion: SdkVersion?, ) : SentryOptions.BeforeSendCallback { - override fun execute(event: SentryEvent, hint: Hint): SentryEvent { + override fun execute( + event: SentryEvent, + hint: Hint, + ): SentryEvent { setEventOriginTag(event) addPackages(event, sdkVersion) return event @@ -362,7 +407,6 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } companion object { - private const val flutterSdk = "sentry.dart.flutter" private const val androidSdk = "sentry.java.android.flutter" private const val nativeSdk = "sentry.native.android.flutter" @@ -379,15 +423,18 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } private fun setEventEnvironmentTag( - event: SentryEvent, - origin: String = "android", - environment: String, + event: SentryEvent, + origin: String = "android", + environment: String, ) { event.setTag("event.origin", origin) event.setTag("event.environment", environment) } - private fun addPackages(event: SentryEvent, sdk: SdkVersion?) { + private fun addPackages( + event: SentryEvent, + sdk: SdkVersion?, + ) { event.sdk?.let { if (it.name == flutterSdk) { sdk?.packageSet?.forEach { sentryPackage -> @@ -409,11 +456,11 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } val currentScope = InternalSentrySdk.getCurrentScope() val serializedScope = - InternalSentrySdk.serializeScope( - context, - options, - currentScope, - ) + InternalSentrySdk.serializeScope( + context, + options, + currentScope, + ) result.success(serializedScope) } } diff --git a/flutter/android/src/test/kotlin/io/sentry/flutter/SentryFlutterTest.kt b/flutter/android/src/test/kotlin/io/sentry/flutter/SentryFlutterTest.kt index 6be5659456..1d8141f163 100644 --- a/flutter/android/src/test/kotlin/io/sentry/flutter/SentryFlutterTest.kt +++ b/flutter/android/src/test/kotlin/io/sentry/flutter/SentryFlutterTest.kt @@ -113,35 +113,37 @@ class SentryFlutterTest { class Fixture { var options = SentryAndroidOptions() - val data = mapOf( - "dsn" to "fixture-dsn", - "debug" to true, - "environment" to "fixture-environment", - "release" to "fixture-release", - "dist" to "fixture-dist", - "enableAutoSessionTracking" to false, - "autoSessionTrackingIntervalMillis" to 9001L, - "anrTimeoutIntervalMillis" to 9002L, - "attachThreads" to true, - "attachStacktrace" to false, - "enableAutoNativeBreadcrumbs" to false, - "maxBreadcrumbs" to 9003, - "maxCacheItems" to 9004, - "anrEnabled" to false, - "sendDefaultPii" to true, - "enableNdkScopeSync" to false, - "proguardUuid" to "fixture-proguardUuid", - "enableNativeCrashHandling" to false, - "sendClientReports" to false, - "maxAttachmentSize" to 9005L, - "enableAutoPerformanceTracing" to true, - "connectionTimeoutMillis" to 9006, - "readTimeoutMillis" to 9007, - "replay" to mapOf( - "sessionSampleRate" to 0.5, - "errorSampleRate" to 0.6, + val data = + mapOf( + "dsn" to "fixture-dsn", + "debug" to true, + "environment" to "fixture-environment", + "release" to "fixture-release", + "dist" to "fixture-dist", + "enableAutoSessionTracking" to false, + "autoSessionTrackingIntervalMillis" to 9001L, + "anrTimeoutIntervalMillis" to 9002L, + "attachThreads" to true, + "attachStacktrace" to false, + "enableAutoNativeBreadcrumbs" to false, + "maxBreadcrumbs" to 9003, + "maxCacheItems" to 9004, + "anrEnabled" to false, + "sendDefaultPii" to true, + "enableNdkScopeSync" to false, + "proguardUuid" to "fixture-proguardUuid", + "enableNativeCrashHandling" to false, + "sendClientReports" to false, + "maxAttachmentSize" to 9005L, + "enableAutoPerformanceTracing" to true, + "connectionTimeoutMillis" to 9006, + "readTimeoutMillis" to 9007, + "replay" to + mapOf( + "sessionSampleRate" to 0.5, + "errorSampleRate" to 0.6, + ), ) - ) fun getSut(): SentryFlutter { return SentryFlutter( From f136795965d8674a38b21d49250bad74b0f231f0 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 2 May 2024 14:08:04 +0200 Subject: [PATCH 27/74] ci fixes --- .../src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt | 1 + flutter/lib/src/native/factory_web.dart | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index 8770d7cc3a..946135a150 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -51,6 +51,7 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { ) } + @Suppress("CyclomaticComplexMethod") override fun onMethodCall( call: MethodCall, result: Result, diff --git a/flutter/lib/src/native/factory_web.dart b/flutter/lib/src/native/factory_web.dart index 8038ea9780..c888dffcff 100644 --- a/flutter/lib/src/native/factory_web.dart +++ b/flutter/lib/src/native/factory_web.dart @@ -4,6 +4,7 @@ import '../../sentry_flutter.dart'; import 'sentry_native_binding.dart'; // This isn't actually called, see SentryFlutter.init() -SentryNativeBinding createBinding(PlatformChecker pc, MethodChannel channel) { +SentryNativeBinding createBinding( + PlatformChecker _, MethodChannel __, SentryFlutterOptions ___) { throw UnsupportedError("Native binding is not supported on this platform."); } From 2aa129cfdd7e5574e217e89652eca2ed2bb86588 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 2 May 2024 14:24:11 +0200 Subject: [PATCH 28/74] fix tests --- flutter/lib/sentry_flutter.dart | 1 + .../init_native_sdk_integration_test.dart | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/flutter/lib/sentry_flutter.dart b/flutter/lib/sentry_flutter.dart index d15c8b7a70..bea9016630 100644 --- a/flutter/lib/sentry_flutter.dart +++ b/flutter/lib/sentry_flutter.dart @@ -8,6 +8,7 @@ export 'src/integrations/load_release_integration.dart'; export 'src/navigation/sentry_navigator_observer.dart'; export 'src/sentry_flutter.dart'; export 'src/sentry_flutter_options.dart'; +export 'src/sentry_replay_options.dart'; export 'src/flutter_sentry_attachment.dart'; export 'src/sentry_asset_bundle.dart'; export 'src/integrations/on_error_integration.dart'; diff --git a/flutter/test/integrations/init_native_sdk_integration_test.dart b/flutter/test/integrations/init_native_sdk_integration_test.dart index b41c2d51cf..fbd3d1b3f4 100644 --- a/flutter/test/integrations/init_native_sdk_integration_test.dart +++ b/flutter/test/integrations/init_native_sdk_integration_test.dart @@ -64,6 +64,10 @@ void main() { 'connectionTimeoutMillis': 5000, 'readTimeoutMillis': 5000, 'appHangTimeoutIntervalMillis': 2000, + 'replay': { + 'sessionSampleRate': null, + 'errorSampleRate': null, + }, }); }); @@ -104,7 +108,9 @@ void main() { ..enableAppHangTracking = false ..connectionTimeout = Duration(milliseconds: 9001) ..readTimeout = Duration(milliseconds: 9002) - ..appHangTimeoutInterval = Duration(milliseconds: 9003); + ..appHangTimeoutInterval = Duration(milliseconds: 9003) + ..replay.sessionSampleRate = 0.1 + ..replay.errorSampleRate = 0.2; options.sdk.addIntegration('foo'); options.sdk.addPackage('bar', '1'); @@ -149,6 +155,10 @@ void main() { 'connectionTimeoutMillis': 9001, 'readTimeoutMillis': 9002, 'appHangTimeoutIntervalMillis': 9003, + 'replay': { + 'sessionSampleRate': 0.1, + 'errorSampleRate': 0.2, + }, }); }); From 698276da7e5658dc62312c7625a3a295ef3e1e02 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 2 May 2024 20:30:38 +0200 Subject: [PATCH 29/74] add jnigen scripts --- flutter/scripts/generate-android-bindings.sh | 18 ++++++++++++++++++ flutter/scripts/update-android.sh | 1 + 2 files changed, 19 insertions(+) create mode 100755 flutter/scripts/generate-android-bindings.sh diff --git a/flutter/scripts/generate-android-bindings.sh b/flutter/scripts/generate-android-bindings.sh new file mode 100755 index 0000000000..100e56ae95 --- /dev/null +++ b/flutter/scripts/generate-android-bindings.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ -n ${CI:+x} ]]; then + echo "Running in CI so we need to set up Flutter SDK first" + curl -Lv https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_3.13.3-stable.zip --output /tmp/flutter.zip + unzip -q /tmp/flutter.zip -d /tmp + export PATH=":/tmp/flutter/bin:$PATH" + which flutter + flutter --version +fi + +# jnigen requires building the app first +pushd example +flutter build apk +popd + +dart run jnigen --config ./ffi-jni.yaml diff --git a/flutter/scripts/update-android.sh b/flutter/scripts/update-android.sh index 183441bee7..5d053ce47d 100755 --- a/flutter/scripts/update-android.sh +++ b/flutter/scripts/update-android.sh @@ -20,6 +20,7 @@ get-repo) set-version) newValue="${BASH_REMATCH[1]}$2" echo "${content/${BASH_REMATCH[0]}/$newValue}" >$file + ../scripts/generate-android-bindings.sh "$2" ;; *) echo "Unknown argument $1" From c6c3a17803a72d4a33d71689a60bf4713bb68b4d Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 2 May 2024 21:26:18 +0200 Subject: [PATCH 30/74] use android 7.9.0 alpha.1 --- flutter/android/build.gradle | 6 ++---- flutter/example/android/settings.gradle | 18 +++++++++--------- flutter/scripts/generate-android-bindings.sh | 1 + flutter/scripts/update-android.sh | 2 +- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/flutter/android/build.gradle b/flutter/android/build.gradle index 42090f7d52..d51cefa6ae 100644 --- a/flutter/android/build.gradle +++ b/flutter/android/build.gradle @@ -66,10 +66,8 @@ android { } dependencies { - api 'io.sentry:sentry-android:7.8.0' - implementation 'io.sentry:sentry-android-core:7.8.0' - implementation 'io.sentry:sentry-android-ndk:7.8.0' - implementation 'io.sentry:sentry-replay:1.0.0' + api 'io.sentry:sentry-android:7.9.0-alpha.1' + api 'io.sentry:sentry-android-replay:7.9.0-alpha.1' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" // Required -- JUnit 4 framework diff --git a/flutter/example/android/settings.gradle b/flutter/example/android/settings.gradle index df0d4c80c0..0d19a22bf4 100644 --- a/flutter/example/android/settings.gradle +++ b/flutter/example/android/settings.gradle @@ -10,12 +10,12 @@ def flutterSdkPath = properties.getProperty("flutter.sdk") assert flutterSdkPath != null, "flutter.sdk not set in local.properties" apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" -includeBuild("../../../../sentry-java") { - dependencySubstitution { - substitute(module("io.sentry:sentry")).using(project(":sentry")) - substitute(module("io.sentry:sentry-android")).using(project(":sentry-android")) - substitute(module("io.sentry:sentry-android-core")).using(project(":sentry-android-core")) - substitute(module("io.sentry:sentry-android-ndk")).using(project(":sentry-android-ndk")) - substitute(module("io.sentry:sentry-replay")).using(project(":sentry-android-replay")) - } -} +// includeBuild("../../../../sentry-java") { +// dependencySubstitution { +// substitute(module("io.sentry:sentry")).using(project(":sentry")) +// substitute(module("io.sentry:sentry-android")).using(project(":sentry-android")) +// substitute(module("io.sentry:sentry-android-core")).using(project(":sentry-android-core")) +// substitute(module("io.sentry:sentry-android-ndk")).using(project(":sentry-android-ndk")) +// substitute(module("io.sentry:sentry-replay")).using(project(":sentry-android-replay")) +// } +// } diff --git a/flutter/scripts/generate-android-bindings.sh b/flutter/scripts/generate-android-bindings.sh index 100e56ae95..88fe2ece05 100755 --- a/flutter/scripts/generate-android-bindings.sh +++ b/flutter/scripts/generate-android-bindings.sh @@ -11,6 +11,7 @@ if [[ -n ${CI:+x} ]]; then fi # jnigen requires building the app first +cd $(dirname "$0")/../ pushd example flutter build apk popd diff --git a/flutter/scripts/update-android.sh b/flutter/scripts/update-android.sh index 5d053ce47d..60775a6eec 100755 --- a/flutter/scripts/update-android.sh +++ b/flutter/scripts/update-android.sh @@ -4,7 +4,7 @@ set -euo pipefail cd $(dirname "$0")/../android file='build.gradle' content=$(cat $file) -regex='(io\.sentry:sentry-android:)([0-9\.]+)' +regex='(io\.sentry:sentry-android:)([0-9\.]+(\-[a-z0-9\.]+)?)' if ! [[ $content =~ $regex ]]; then echo "Failed to find the android plugin version in $file" exit 1 From cf8ed52a2a8f4d8309450a49549c3c36b0ed1b90 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 2 May 2024 22:55:07 +0200 Subject: [PATCH 31/74] move native init & close to SentryNative --- .../integrations/native_sdk_integration.dart | 77 ++++--------------- flutter/lib/src/native/factory_real.dart | 5 +- flutter/lib/src/native/factory_web.dart | 3 +- .../src/native/java/sentry_native_java.dart | 6 +- flutter/lib/src/native/sentry_native.dart | 5 ++ .../lib/src/native/sentry_native_binding.dart | 3 + .../lib/src/native/sentry_native_channel.dart | 47 +++++++++++ flutter/lib/src/sentry_flutter.dart | 10 ++- .../init_native_sdk_integration_test.dart | 38 ++------- .../native_sdk_integration_test.dart | 73 +++++++++--------- flutter/test/mocks.dart | 30 ++++++++ 11 files changed, 159 insertions(+), 138 deletions(-) diff --git a/flutter/lib/src/integrations/native_sdk_integration.dart b/flutter/lib/src/integrations/native_sdk_integration.dart index a5e26ac3f0..35181afc1e 100644 --- a/flutter/lib/src/integrations/native_sdk_integration.dart +++ b/flutter/lib/src/integrations/native_sdk_integration.dart @@ -1,18 +1,15 @@ import 'dart:async'; -import 'package:flutter/services.dart'; import 'package:sentry/sentry.dart'; -import '../native/factory.dart'; import '../native/sentry_native.dart'; -import '../sentry_flutter.dart'; import '../sentry_flutter_options.dart'; /// Enables Sentry's native SDKs (Android and iOS) with options. class NativeSdkIntegration implements Integration { - NativeSdkIntegration(this._channel); + NativeSdkIntegration(this._native); - final MethodChannel _channel; SentryFlutterOptions? _options; + final SentryNative _native; @override Future call(Hub hub, SentryFlutterOptions options) async { @@ -22,52 +19,8 @@ class NativeSdkIntegration implements Integration { return; } - final binding = createBinding(options.platformChecker, _channel, options); - SentryFlutter.native = SentryNative(options, binding); - try { - await _channel.invokeMethod('initNativeSdk', { - 'dsn': options.dsn, - 'debug': options.debug, - 'environment': options.environment, - 'release': options.release, - 'enableAutoSessionTracking': options.enableAutoSessionTracking, - 'enableNativeCrashHandling': options.enableNativeCrashHandling, - 'attachStacktrace': options.attachStacktrace, - 'attachThreads': options.attachThreads, - 'autoSessionTrackingIntervalMillis': - options.autoSessionTrackingInterval.inMilliseconds, - 'dist': options.dist, - 'integrations': options.sdk.integrations, - 'packages': - options.sdk.packages.map((e) => e.toJson()).toList(growable: false), - 'diagnosticLevel': options.diagnosticLevel.name, - 'maxBreadcrumbs': options.maxBreadcrumbs, - 'anrEnabled': options.anrEnabled, - 'anrTimeoutIntervalMillis': options.anrTimeoutInterval.inMilliseconds, - 'enableAutoNativeBreadcrumbs': options.enableAutoNativeBreadcrumbs, - 'maxCacheItems': options.maxCacheItems, - 'sendDefaultPii': options.sendDefaultPii, - 'enableWatchdogTerminationTracking': - options.enableWatchdogTerminationTracking, - 'enableNdkScopeSync': options.enableNdkScopeSync, - 'enableAutoPerformanceTracing': options.enableAutoPerformanceTracing, - 'sendClientReports': options.sendClientReports, - 'proguardUuid': options.proguardUuid, - 'maxAttachmentSize': options.maxAttachmentSize, - 'recordHttpBreadcrumbs': options.recordHttpBreadcrumbs, - 'captureFailedRequests': options.captureFailedRequests, - 'enableAppHangTracking': options.enableAppHangTracking, - 'connectionTimeoutMillis': options.connectionTimeout.inMilliseconds, - 'readTimeoutMillis': options.readTimeout.inMilliseconds, - 'appHangTimeoutIntervalMillis': - options.appHangTimeoutInterval.inMilliseconds, - 'replay': { - 'sessionSampleRate': options.replay.sessionSampleRate, - 'errorSampleRate': options.replay.errorSampleRate, - }, - }); - + await _native.init(options); options.sdk.addIntegration('nativeSdkIntegration'); } catch (exception, stackTrace) { options.logger( @@ -81,19 +34,17 @@ class NativeSdkIntegration implements Integration { @override Future close() async { - final options = _options; - if (options != null && !options.autoInitializeNativeSdk) { - return; - } - try { - await _channel.invokeMethod('closeNativeSdk'); - } catch (exception, stackTrace) { - _options?.logger( - SentryLevel.fatal, - 'nativeSdkIntegration failed to be closed', - exception: exception, - stackTrace: stackTrace, - ); + if (_options?.autoInitializeNativeSdk == true) { + try { + await _native.close(); + } catch (exception, stackTrace) { + _options?.logger( + SentryLevel.fatal, + 'nativeSdkIntegration failed to be closed', + exception: exception, + stackTrace: stackTrace, + ); + } } } } diff --git a/flutter/lib/src/native/factory_real.dart b/flutter/lib/src/native/factory_real.dart index 80e6005eee..d5ee4f5cca 100644 --- a/flutter/lib/src/native/factory_real.dart +++ b/flutter/lib/src/native/factory_real.dart @@ -6,12 +6,11 @@ import 'java/sentry_native_java.dart'; import 'sentry_native_binding.dart'; import 'sentry_native_channel.dart'; -SentryNativeBinding createBinding( - PlatformChecker pc, MethodChannel channel, SentryFlutterOptions options) { +SentryNativeBinding createBinding(PlatformChecker pc, MethodChannel channel) { if (pc.platform.isIOS || pc.platform.isMacOS) { return SentryNativeCocoa(channel); } else if (pc.platform.isAndroid) { - return SentryNativeJava(channel, options); + return SentryNativeJava(channel); } else { return SentryNativeChannel(channel); } diff --git a/flutter/lib/src/native/factory_web.dart b/flutter/lib/src/native/factory_web.dart index c888dffcff..c2cd57ada7 100644 --- a/flutter/lib/src/native/factory_web.dart +++ b/flutter/lib/src/native/factory_web.dart @@ -4,7 +4,6 @@ import '../../sentry_flutter.dart'; import 'sentry_native_binding.dart'; // This isn't actually called, see SentryFlutter.init() -SentryNativeBinding createBinding( - PlatformChecker _, MethodChannel __, SentryFlutterOptions ___) { +SentryNativeBinding createBinding(PlatformChecker _, MethodChannel __) { throw UnsupportedError("Native binding is not supported on this platform."); } diff --git a/flutter/lib/src/native/java/sentry_native_java.dart b/flutter/lib/src/native/java/sentry_native_java.dart index b7ec1223b7..f83b1879cb 100644 --- a/flutter/lib/src/native/java/sentry_native_java.dart +++ b/flutter/lib/src/native/java/sentry_native_java.dart @@ -8,7 +8,10 @@ import 'binding.dart' as java; @internal class SentryNativeJava extends SentryNativeChannel { - SentryNativeJava(super.channel, SentryFlutterOptions options) { + SentryNativeJava(super.channel); + + @override + Future init(SentryFlutterOptions options) async { if (options.replay.isEnabled) { // Necessary for the generated binding to work as of jnigen v0.6.0 // This may change in the future. @@ -16,5 +19,6 @@ class SentryNativeJava extends SentryNativeChannel { java.SentryFlutterReplay.recorder = AndroidReplayRecorder.create(options); } + return super.init(options); } } diff --git a/flutter/lib/src/native/sentry_native.dart b/flutter/lib/src/native/sentry_native.dart index a7973f8a12..9f2f2df49e 100644 --- a/flutter/lib/src/native/sentry_native.dart +++ b/flutter/lib/src/native/sentry_native.dart @@ -30,6 +30,11 @@ class SentryNative { /// Flag indicating if app start measurement was added to the first transaction. bool didAddAppStartMeasurement = false; + Future init(SentryFlutterOptions options) async => + _invoke("init", () => _binding.init(options)); + + Future close() async => _invoke("close", _binding.close); + /// Fetch [NativeAppStart] from native channels. Can only be called once. Future fetchNativeAppStart() async { _didFetchAppStart = true; diff --git a/flutter/lib/src/native/sentry_native_binding.dart b/flutter/lib/src/native/sentry_native_binding.dart index 54d335d529..950e7f9994 100644 --- a/flutter/lib/src/native/sentry_native_binding.dart +++ b/flutter/lib/src/native/sentry_native_binding.dart @@ -9,6 +9,9 @@ import 'sentry_native.dart'; @internal abstract class SentryNativeBinding { // TODO Move other native calls here. + Future init(SentryFlutterOptions options); + + Future close(); Future fetchNativeAppStart(); diff --git a/flutter/lib/src/native/sentry_native_channel.dart b/flutter/lib/src/native/sentry_native_channel.dart index 4bf9745cb3..6961550229 100644 --- a/flutter/lib/src/native/sentry_native_channel.dart +++ b/flutter/lib/src/native/sentry_native_channel.dart @@ -17,6 +17,53 @@ class SentryNativeChannel implements SentryNativeBinding { // TODO Move other native calls here. + @override + Future init(SentryFlutterOptions options) async => + _channel.invokeMethod('initNativeSdk', { + 'dsn': options.dsn, + 'debug': options.debug, + 'environment': options.environment, + 'release': options.release, + 'enableAutoSessionTracking': options.enableAutoSessionTracking, + 'enableNativeCrashHandling': options.enableNativeCrashHandling, + 'attachStacktrace': options.attachStacktrace, + 'attachThreads': options.attachThreads, + 'autoSessionTrackingIntervalMillis': + options.autoSessionTrackingInterval.inMilliseconds, + 'dist': options.dist, + 'integrations': options.sdk.integrations, + 'packages': + options.sdk.packages.map((e) => e.toJson()).toList(growable: false), + 'diagnosticLevel': options.diagnosticLevel.name, + 'maxBreadcrumbs': options.maxBreadcrumbs, + 'anrEnabled': options.anrEnabled, + 'anrTimeoutIntervalMillis': options.anrTimeoutInterval.inMilliseconds, + 'enableAutoNativeBreadcrumbs': options.enableAutoNativeBreadcrumbs, + 'maxCacheItems': options.maxCacheItems, + 'sendDefaultPii': options.sendDefaultPii, + 'enableWatchdogTerminationTracking': + options.enableWatchdogTerminationTracking, + 'enableNdkScopeSync': options.enableNdkScopeSync, + 'enableAutoPerformanceTracing': options.enableAutoPerformanceTracing, + 'sendClientReports': options.sendClientReports, + 'proguardUuid': options.proguardUuid, + 'maxAttachmentSize': options.maxAttachmentSize, + 'recordHttpBreadcrumbs': options.recordHttpBreadcrumbs, + 'captureFailedRequests': options.captureFailedRequests, + 'enableAppHangTracking': options.enableAppHangTracking, + 'connectionTimeoutMillis': options.connectionTimeout.inMilliseconds, + 'readTimeoutMillis': options.readTimeout.inMilliseconds, + 'appHangTimeoutIntervalMillis': + options.appHangTimeoutInterval.inMilliseconds, + 'replay': { + 'sessionSampleRate': options.replay.sessionSampleRate, + 'errorSampleRate': options.replay.errorSampleRate, + }, + }); + + @override + Future close() async => _channel.invokeMethod('closeNativeSdk'); + @override Future fetchNativeAppStart() async { final json = diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index 9173b20e92..9da26e5102 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -12,6 +12,7 @@ import 'event_processor/widget_event_processor.dart'; import 'frame_callback_handler.dart'; import 'integrations/connectivity/connectivity_integration.dart'; import 'integrations/screenshot_integration.dart'; +import 'native/factory.dart'; import 'native/native_scope_observer.dart'; import 'profiling.dart'; import 'renderer/renderer.dart'; @@ -52,6 +53,11 @@ mixin SentryFlutter { final platformDispatcher = PlatformDispatcher.instance; final wrapper = PlatformDispatcherWrapper(platformDispatcher); + if (platformChecker?.hasNativeIntegration == true) { + final binding = createBinding(flutterOptions.platformChecker, channel); + _native = SentryNative(flutterOptions, binding); + } + // Flutter Web don't capture [Future] errors if using [PlatformDispatcher.onError] and not // the [runZonedGuarded]. // likely due to https://github.com/flutter/flutter/issues/100277 @@ -147,8 +153,8 @@ mixin SentryFlutter { // The ordering here matters, as we'd like to first start the native integration. // That allow us to send events to the network and then the Flutter integrations. // Flutter Web doesn't need that, only Android and iOS. - if (platformChecker.hasNativeIntegration) { - integrations.add(NativeSdkIntegration(channel)); + if (_native != null) { + integrations.add(NativeSdkIntegration(_native!)); } // Will enrich events with device context, native packages and integrations diff --git a/flutter/test/integrations/init_native_sdk_integration_test.dart b/flutter/test/integrations/init_native_sdk_integration_test.dart index fbd3d1b3f4..25be09f8ec 100644 --- a/flutter/test/integrations/init_native_sdk_integration_test.dart +++ b/flutter/test/integrations/init_native_sdk_integration_test.dart @@ -3,13 +3,13 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:sentry_flutter/src/integrations/native_sdk_integration.dart'; +import 'package:sentry_flutter/src/native/sentry_native_channel.dart'; import 'package:sentry_flutter/src/version.dart'; import '../mocks.dart'; void main() { - group(NativeSdkIntegration, () { + group('initNativeSdk', () { late Fixture fixture; setUp(() { fixture = Fixture(); @@ -25,7 +25,7 @@ void main() { }); var sut = fixture.getSut(channel); - await sut.call(HubAdapter(), createOptions()); + await sut.init(createOptions()); channel.setMethodCallHandler(null); @@ -115,7 +115,7 @@ void main() { options.sdk.addIntegration('foo'); options.sdk.addPackage('bar', '1'); - await sut.call(HubAdapter(), options); + await sut.init(options); channel.setMethodCallHandler(null); @@ -161,32 +161,6 @@ void main() { }, }); }); - - test('adds integration', () async { - final channel = createChannelWithCallback((call) async {}); - var sut = fixture.getSut(channel); - - final options = createOptions(); - await sut.call(HubAdapter(), options); - - expect(options.sdk.integrations, ['nativeSdkIntegration']); - - channel.setMethodCallHandler(null); - }); - - test('integration is not added in case of an exception', () async { - final channel = createChannelWithCallback((call) async { - throw Exception('foo'); - }); - var sut = fixture.getSut(channel); - - final options = createOptions(); - await sut.call(NoOpHub(), options); - - expect(options.sdk.integrations, []); - - channel.setMethodCallHandler(null); - }); }); } @@ -214,7 +188,7 @@ SentryFlutterOptions createOptions() { } class Fixture { - NativeSdkIntegration getSut(MethodChannel channel) { - return NativeSdkIntegration(channel); + SentryNativeChannel getSut(MethodChannel native) { + return SentryNativeChannel(native); } } diff --git a/flutter/test/integrations/native_sdk_integration_test.dart b/flutter/test/integrations/native_sdk_integration_test.dart index e48a22933a..54bfc2ad1c 100644 --- a/flutter/test/integrations/native_sdk_integration_test.dart +++ b/flutter/test/integrations/native_sdk_integration_test.dart @@ -27,22 +27,17 @@ void main() { test('nativeSdkIntegration adds integration', () async { // ignore: deprecated_member_use _channel.setMockMethodCallHandler((MethodCall methodCall) async {}); - - final integration = NativeSdkIntegration(_channel); + final mock = TestMockSentryNative(); + final integration = NativeSdkIntegration(mock); await integration(fixture.hub, fixture.options); - expect(fixture.options.sdk.integrations.contains('nativeSdkIntegration'), - true); + expect(fixture.options.sdk.integrations, contains('nativeSdkIntegration')); + expect(mock.numberOfInitCalls, 1); }); test('nativeSdkIntegration do not throw', () async { - // ignore: deprecated_member_use - _channel.setMockMethodCallHandler((MethodCall methodCall) async { - throw Exception(); - }); - - final integration = NativeSdkIntegration(_channel); + final integration = NativeSdkIntegration(_ThrowingMockSentryNative()); await integration(fixture.hub, fixture.options); @@ -51,51 +46,52 @@ void main() { }); test('nativeSdkIntegration closes native SDK', () async { - var closeCalled = false; - // ignore: deprecated_member_use - _channel.setMockMethodCallHandler((MethodCall methodCall) async { - expect(methodCall.method, 'closeNativeSdk'); - closeCalled = true; - }); - - final integration = NativeSdkIntegration(_channel); + final mock = TestMockSentryNative(); + final integration = NativeSdkIntegration(mock); + await integration.call(fixture.hub, fixture.options); await integration.close(); - expect(closeCalled, true); + expect(mock.numberOfCloseCalls, 1); }); test('nativeSdkIntegration does not call native sdk when auto init disabled', () async { - var methodChannelCalled = false; - // ignore: deprecated_member_use - _channel.setMockMethodCallHandler((MethodCall methodCall) async { - methodChannelCalled = true; - }); + final mock = TestMockSentryNative(); + final integration = NativeSdkIntegration(mock); fixture.options.autoInitializeNativeSdk = false; - final integration = NativeSdkIntegration(_channel); - await integration.call(fixture.hub, fixture.options); - expect(methodChannelCalled, false); + expect(mock.numberOfInitCalls, 0); }); test('nativeSdkIntegration does not close native when auto init disabled', () async { - var methodChannelCalled = false; - // ignore: deprecated_member_use - _channel.setMockMethodCallHandler((MethodCall methodCall) async { - methodChannelCalled = true; - }); + final mock = TestMockSentryNative(); + final integration = NativeSdkIntegration(mock); fixture.options.autoInitializeNativeSdk = false; - final integration = NativeSdkIntegration(_channel); - await integration(fixture.hub, fixture.options); await integration.close(); - expect(methodChannelCalled, false); + expect(mock.numberOfCloseCalls, 0); + }); + + test('adds integration', () async { + final mock = TestMockSentryNative(); + final integration = NativeSdkIntegration(mock); + + await integration.call(fixture.hub, fixture.options); + + expect(fixture.options.sdk.integrations, ['nativeSdkIntegration']); + }); + + test('integration is not added in case of an exception', () async { + final integration = NativeSdkIntegration(_ThrowingMockSentryNative()); + + await integration.call(fixture.hub, fixture.options); + expect(fixture.options.sdk.integrations, []); }); } @@ -103,3 +99,10 @@ class Fixture { final hub = MockHub(); final options = SentryFlutterOptions(dsn: fakeDsn); } + +class _ThrowingMockSentryNative extends TestMockSentryNative { + @override + Future init(SentryFlutterOptions options) async { + throw Exception(); + } +} diff --git a/flutter/test/mocks.dart b/flutter/test/mocks.dart index 0520a09884..a373ee7511 100644 --- a/flutter/test/mocks.dart +++ b/flutter/test/mocks.dart @@ -199,6 +199,9 @@ class TestMockSentryNative implements SentryNative { var numberOfStartProfilerCalls = 0; var numberOfDiscardProfilerCalls = 0; var numberOfCollectProfileCalls = 0; + var numberOfInitCalls = 0; + SentryFlutterOptions? initOptions; + var numberOfCloseCalls = 0; @override Future addBreadcrumb(Breadcrumb breadcrumb) async { @@ -294,6 +297,19 @@ class TestMockSentryNative implements SentryNative { numberOfDiscardProfilerCalls++; return Future.value(null); } + + @override + Future init(SentryFlutterOptions options) { + numberOfInitCalls++; + initOptions = options; + return Future.value(null); + } + + @override + Future close() { + numberOfCloseCalls++; + return Future.value(null); + } } // TODO can this be replaced with https://pub.dev/packages/mockito#verifying-exact-number-of-invocations--at-least-x--never @@ -316,6 +332,8 @@ class MockNativeChannel implements SentryNativeBinding { int numberOfStartProfilerCalls = 0; int numberOfDiscardProfilerCalls = 0; int numberOfCollectProfileCalls = 0; + int numberOfInitCalls = 0; + int numberOfCloseCalls = 0; @override Future fetchNativeAppStart() async => nativeAppStart; @@ -395,6 +413,18 @@ class MockNativeChannel implements SentryNativeBinding { numberOfDiscardProfilerCalls++; return Future.value(null); } + + @override + Future init(SentryFlutterOptions options) { + numberOfInitCalls++; + return Future.value(null); + } + + @override + Future close() { + numberOfCloseCalls++; + return Future.value(null); + } } class MockRendererWrapper implements RendererWrapper { From 55c7056d3541165ce1a8b043e3ad9590f744d4e6 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 2 May 2024 23:00:22 +0200 Subject: [PATCH 32/74] cleanup --- .../SentryFlutterReplayIntegration.swift | 56 +------------------ 1 file changed, 2 insertions(+), 54 deletions(-) diff --git a/flutter/ios/Classes/SentryFlutterReplayIntegration.swift b/flutter/ios/Classes/SentryFlutterReplayIntegration.swift index 6969999a78..9bfe56c554 100644 --- a/flutter/ios/Classes/SentryFlutterReplayIntegration.swift +++ b/flutter/ios/Classes/SentryFlutterReplayIntegration.swift @@ -6,62 +6,10 @@ class SentryFlutterReplayIntegration: NSObject, SentryIntegrationProtocol { // private var sessionReplay: SentrySessionReplay = null func install(with options: Options) -> Bool { - // if #available(iOS 16.0, tvOS 16.0, *) { - // guard let replayOptions = options.experimental.sessionReplay else { - // return false - // } - - // let shouldReplayFullSession = SentryDependencyContainer.sharedInstance.random.nextNumber() < replayOptions.sessionSampleRate - // if !shouldReplayFullSession && replayOptions.errorSampleRate == 0 { - // return false - // } - - // let replayDir = URL(fileURLWithPath: SentryDependencyContainer.sharedInstance.fileManager.sentryPath) - // .appendingPathComponent(SENTRY_REPLAY_FOLDER) - // .appendingPathComponent(UUID().uuidString) - - // if !FileManager.default.fileExists(atPath: replayDir.path) { - // try? FileManager.default.createDirectory(at: replayDir, withIntermediateDirectories: true, attributes: nil) - // } - - // let replayMaker = SentryOnDemandReplay(outputPath: replayDir.path) - // replayMaker.bitRate = replayOptions.replayBitRate - // replayMaker.cacheMaxSize = shouldReplayFullSession ? replayOptions.sessionSegmentDuration : replayOptions.errorReplayDuration - - // self.sessionReplay = SentrySessionReplay( - // settings: replayOptions, - // replayFolderPath: replayDir, - // screenshotProvider: SentryViewPhotographer.shared, - // replayMaker: replayMaker, - // dateProvider: SentryDependencyContainer.sharedInstance.dateProvider, - // random: SentryDependencyContainer.sharedInstance.random, - // displayLinkWrapper: SentryDisplayLinkWrapper() - // ) - - // self.sessionReplay.start( - // SentryDependencyContainer.sharedInstance.application.windows.first, - // fullSession: shouldReplayFullSession - // ) - - // NotificationCenter.default.addObserver( - // self, - // selector: #selector(stop), - // name: UIApplication.didEnterBackgroundNotification, - // object: nil - // ) - - // SentryGlobalEventProcessor.shared.addEventProcessor { event in - // self.sessionReplay.captureReplay(for: event) - // return event - // } - - // return true - // } else { - return false - // } + // TODO not implemented yet + return false } func uninstall() { - // self.sessionReplay.stop() } } From ff2a8edeb3665774a499e0a9b8997471671420f6 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 6 May 2024 12:26:25 +0200 Subject: [PATCH 33/74] add macOS integration link --- flutter/macos/Classes/SentryFlutterReplayIntegration.swift | 1 + 1 file changed, 1 insertion(+) create mode 120000 flutter/macos/Classes/SentryFlutterReplayIntegration.swift diff --git a/flutter/macos/Classes/SentryFlutterReplayIntegration.swift b/flutter/macos/Classes/SentryFlutterReplayIntegration.swift new file mode 120000 index 0000000000..ea2c5d8041 --- /dev/null +++ b/flutter/macos/Classes/SentryFlutterReplayIntegration.swift @@ -0,0 +1 @@ +../../ios/Classes/SentryFlutterReplayIntegration.swift \ No newline at end of file From c3b60aae394875f6295cd09f97f17d8653563673 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 6 May 2024 13:14:18 +0200 Subject: [PATCH 34/74] rollback cocoa changes --- .../Classes/SentryFlutterPluginApple.swift | 21 +++++++------------ .../SentryFlutterReplayIntegration.swift | 15 ------------- .../SentryFlutterReplayIntegration.swift | 1 - 3 files changed, 7 insertions(+), 30 deletions(-) delete mode 100644 flutter/ios/Classes/SentryFlutterReplayIntegration.swift delete mode 120000 flutter/macos/Classes/SentryFlutterReplayIntegration.swift diff --git a/flutter/ios/Classes/SentryFlutterPluginApple.swift b/flutter/ios/Classes/SentryFlutterPluginApple.swift index a4151f5799..97a10c3324 100644 --- a/flutter/ios/Classes/SentryFlutterPluginApple.swift +++ b/flutter/ios/Classes/SentryFlutterPluginApple.swift @@ -268,9 +268,9 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin { if arguments["enableAutoPerformanceTracing"] as? Bool ?? false { PrivateSentrySDKOnly.appStartMeasurementHybridSDKMode = true -#if os(iOS) || targetEnvironment(macCatalyst) + #if os(iOS) || targetEnvironment(macCatalyst) PrivateSentrySDKOnly.framesTrackingMeasurementHybridSDKMode = true -#endif + #endif } let version = PrivateSentrySDKOnly.getSdkVersionString() @@ -301,24 +301,17 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin { return event } - - // Remove the default replay integration, we'll replace it with a custom, flutter-specific one. - var integrations = options.integrations!.filter { (name) -> Bool in - return name != "SentrySessionReplayIntegration" - } - integrations.append(NSStringFromClass(SentryFlutterReplayIntegration.self)) - options.integrations = integrations } - if didReceiveDidBecomeActiveNotification && + if didReceiveDidBecomeActiveNotification && (PrivateSentrySDKOnly.options.enableAutoSessionTracking || PrivateSentrySDKOnly.options.enableWatchdogTerminationTracking) { // We send a SentryHybridSdkDidBecomeActive to the Sentry Cocoa SDK, so the SDK will mimics // the didBecomeActiveNotification notification. This is needed for session and OOM tracking. - NotificationCenter.default.post(name: Notification.Name("SentryHybridSdkDidBecomeActive"), object: nil) - // we reset the flag for the sake of correctness - didReceiveDidBecomeActiveNotification = false - } + NotificationCenter.default.post(name: Notification.Name("SentryHybridSdkDidBecomeActive"), object: nil) + // we reset the flag for the sake of correctness + didReceiveDidBecomeActiveNotification = false + } result("") } diff --git a/flutter/ios/Classes/SentryFlutterReplayIntegration.swift b/flutter/ios/Classes/SentryFlutterReplayIntegration.swift deleted file mode 100644 index 9bfe56c554..0000000000 --- a/flutter/ios/Classes/SentryFlutterReplayIntegration.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Foundation -import Sentry - -@objc -class SentryFlutterReplayIntegration: NSObject, SentryIntegrationProtocol { - // private var sessionReplay: SentrySessionReplay = null - - func install(with options: Options) -> Bool { - // TODO not implemented yet - return false - } - - func uninstall() { - } -} diff --git a/flutter/macos/Classes/SentryFlutterReplayIntegration.swift b/flutter/macos/Classes/SentryFlutterReplayIntegration.swift deleted file mode 120000 index ea2c5d8041..0000000000 --- a/flutter/macos/Classes/SentryFlutterReplayIntegration.swift +++ /dev/null @@ -1 +0,0 @@ -../../ios/Classes/SentryFlutterReplayIntegration.swift \ No newline at end of file From 3f6d05eb802f0ff9f392788711ba4504f3c9c746 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 6 May 2024 13:32:13 +0200 Subject: [PATCH 35/74] remove jni/jnigen --- flutter/android/build.gradle | 6 - flutter/android/src/main/cpp/.clang-format | 15 - flutter/android/src/main/cpp/CMakeLists.txt | 32 - flutter/android/src/main/cpp/dartjni.h | 424 ---- .../src/main/cpp/sentry_android_binding.c | 1800 ---------------- flutter/ffi-jni.yaml | 27 - flutter/lib/src/native/java/binding.dart | 1802 ----------------- .../src/native/java/sentry_native_java.dart | 18 +- flutter/pubspec.yaml | 2 - 9 files changed, 2 insertions(+), 4124 deletions(-) delete mode 100644 flutter/android/src/main/cpp/.clang-format delete mode 100644 flutter/android/src/main/cpp/CMakeLists.txt delete mode 100644 flutter/android/src/main/cpp/dartjni.h delete mode 100644 flutter/android/src/main/cpp/sentry_android_binding.c delete mode 100644 flutter/ffi-jni.yaml delete mode 100644 flutter/lib/src/native/java/binding.dart diff --git a/flutter/android/build.gradle b/flutter/android/build.gradle index d51cefa6ae..e962963221 100644 --- a/flutter/android/build.gradle +++ b/flutter/android/build.gradle @@ -57,12 +57,6 @@ android { jvmTarget = JavaVersion.VERSION_1_8 languageVersion = "1.4" } - - externalNativeBuild { - cmake { - path "src/main/cpp/CMakeLists.txt" - } - } } dependencies { diff --git a/flutter/android/src/main/cpp/.clang-format b/flutter/android/src/main/cpp/.clang-format deleted file mode 100644 index a256c2f097..0000000000 --- a/flutter/android/src/main/cpp/.clang-format +++ /dev/null @@ -1,15 +0,0 @@ -# From dart SDK: https://github.com/dart-lang/sdk/blob/main/.clang-format - -# Defines the Chromium style for automatic reformatting. -# http://clang.llvm.org/docs/ClangFormatStyleOptions.html -BasedOnStyle: Chromium - -# clang-format doesn't seem to do a good job of this for longer comments. -ReflowComments: 'false' - -# We have lots of these. Though we need to put them all in curly braces, -# clang-format can't do that. -AllowShortIfStatementsOnASingleLine: 'true' - -# Put escaped newlines into the rightmost column. -AlignEscapedNewlinesLeft: false diff --git a/flutter/android/src/main/cpp/CMakeLists.txt b/flutter/android/src/main/cpp/CMakeLists.txt deleted file mode 100644 index a0d5849c59..0000000000 --- a/flutter/android/src/main/cpp/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -# jni_native_build (Build with jni:setup. Do not delete this line.) - -# The Flutter tooling requires that developers have CMake 3.10 or later -# installed. You should not increase this version, as doing so will cause -# the plugin to fail to compile for some customers of the plugin. -cmake_minimum_required(VERSION 3.10) - -project(sentry_android_binding VERSION 0.0.1 LANGUAGES C) - -add_library(sentry_android_binding SHARED - "./sentry_android_binding.c" -) - -set_target_properties(sentry_android_binding PROPERTIES - OUTPUT_NAME "sentry_android_binding" -) - -target_compile_definitions(sentry_android_binding PUBLIC DART_SHARED_LIB) - -if(WIN32) - set_target_properties(${TARGET_NAME} PROPERTIES - LINK_FLAGS "/DELAYLOAD:jvm.dll") -endif() - -if (ANDROID) - target_link_libraries(sentry_android_binding log) -else() - find_package(Java REQUIRED) - find_package(JNI REQUIRED) - include_directories(${JNI_INCLUDE_DIRS}) - target_link_libraries(sentry_android_binding ${JNI_LIBRARIES}) -endif() diff --git a/flutter/android/src/main/cpp/dartjni.h b/flutter/android/src/main/cpp/dartjni.h deleted file mode 100644 index 8f1dc7481c..0000000000 --- a/flutter/android/src/main/cpp/dartjni.h +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -#pragma once - -// Note: include appropriate system jni.h as found by CMake, not third_party/jni.h. -#include -#include -#include -#include - -#if _WIN32 -#include -#else -#include -#include -#endif - -#if _WIN32 -#define FFI_PLUGIN_EXPORT __declspec(dllexport) -#else -#define FFI_PLUGIN_EXPORT -#endif - -#if defined _WIN32 -#define thread_local __declspec(thread) -#else -#define thread_local __thread -#endif - -#ifdef __ANDROID__ -#include -#endif - -#ifdef __ANDROID__ -#define __ENVP_CAST (JNIEnv**) -#else -#define __ENVP_CAST (void**) -#endif - -/// Locking functions for windows and pthread. - -#if defined _WIN32 -#include - -typedef CRITICAL_SECTION MutexLock; -typedef CONDITION_VARIABLE ConditionVariable; - -static inline void init_lock(MutexLock* lock) { - InitializeCriticalSection(lock); -} - -static inline void acquire_lock(MutexLock* lock) { - EnterCriticalSection(lock); -} - -static inline void release_lock(MutexLock* lock) { - LeaveCriticalSection(lock); -} - -static inline void destroy_lock(MutexLock* lock) { - DeleteCriticalSection(lock); -} - -static inline void init_cond(ConditionVariable* cond) { - InitializeConditionVariable(cond); -} - -static inline void signal_cond(ConditionVariable* cond) { - WakeConditionVariable(cond); -} - -static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { - SleepConditionVariableCS(cond, lock, INFINITE); -} - -static inline void destroy_cond(ConditionVariable* cond) { - // Not available. -} - -#elif defined __APPLE__ || defined __LINUX__ || defined __ANDROID__ || \ - defined __GNUC__ -#include - -typedef pthread_mutex_t MutexLock; -typedef pthread_cond_t ConditionVariable; - -static inline void init_lock(MutexLock* lock) { - pthread_mutex_init(lock, NULL); -} - -static inline void acquire_lock(MutexLock* lock) { - pthread_mutex_lock(lock); -} - -static inline void release_lock(MutexLock* lock) { - pthread_mutex_unlock(lock); -} - -static inline void destroy_lock(MutexLock* lock) { - pthread_mutex_destroy(lock); -} - -static inline void init_cond(ConditionVariable* cond) { - pthread_cond_init(cond, NULL); -} - -static inline void signal_cond(ConditionVariable* cond) { - pthread_cond_signal(cond); -} - -static inline void wait_for(ConditionVariable* cond, MutexLock* lock) { - pthread_cond_wait(cond, lock); -} - -static inline void destroy_cond(ConditionVariable* cond) { - pthread_cond_destroy(cond); -} - -#else - -#error "No locking/condition variable support; Possibly unsupported platform" - -#endif - -typedef struct CallbackResult { - MutexLock lock; - ConditionVariable cond; - int ready; - jobject object; -} CallbackResult; - -typedef struct JniLocks { - MutexLock classLoadingLock; - MutexLock methodLoadingLock; - MutexLock fieldLoadingLock; -} JniLocks; - -/// Represents the error when dart-jni layer has already spawned singleton VM. -#define DART_JNI_SINGLETON_EXISTS (-99); - -/// Stores the global state of the JNI. -typedef struct JniContext { - JavaVM* jvm; - jobject classLoader; - jmethodID loadClassMethod; - jobject currentActivity; - jobject appContext; - JniLocks locks; -} JniContext; - -// jniEnv for this thread, used by inline functions in this header, -// therefore declared as extern. -extern thread_local JNIEnv* jniEnv; - -extern JniContext* jni; - -/// Types used by JNI API to distinguish between primitive types. -enum JniType { - booleanType = 0, - byteType = 1, - shortType = 2, - charType = 3, - intType = 4, - longType = 5, - floatType = 6, - doubleType = 7, - objectType = 8, - voidType = 9, -}; - -/// Result type for use by JNI. -/// -/// If [exception] is null, it means the result is valid. -/// It's assumed that the caller knows the expected type in [result]. -typedef struct JniResult { - jvalue value; - jthrowable exception; -} JniResult; - -/// Similar to [JniResult] but for class lookups. -typedef struct JniClassLookupResult { - jclass value; - jthrowable exception; -} JniClassLookupResult; - -/// Similar to [JniResult] but for method/field ID lookups. -typedef struct JniPointerResult { - const void* value; - jthrowable exception; -} JniPointerResult; - -/// JniExceptionDetails holds 2 jstring objects, one is the result of -/// calling `toString` on exception object, other is stack trace; -typedef struct JniExceptionDetails { - jstring message; - jstring stacktrace; -} JniExceptionDetails; - -/// This struct contains functions which wrap method call / field access conveniently along with -/// exception checking. -/// -/// Flutter embedding checks for pending JNI exceptions before an FFI transition, which requires us -/// to check for and clear the exception before returning to dart code, which requires these functions -/// to return result types. -typedef struct JniAccessorsStruct { - JniClassLookupResult (*getClass)(char* internalName); - JniPointerResult (*getFieldID)(jclass cls, char* fieldName, char* signature); - JniPointerResult (*getStaticFieldID)(jclass cls, - char* fieldName, - char* signature); - JniPointerResult (*getMethodID)(jclass cls, - char* methodName, - char* signature); - JniPointerResult (*getStaticMethodID)(jclass cls, - char* methodName, - char* signature); - JniResult (*newObject)(jclass cls, jmethodID ctor, jvalue* args); - JniResult (*newPrimitiveArray)(jsize length, int type); - JniResult (*newObjectArray)(jsize length, - jclass elementClass, - jobject initialElement); - JniResult (*getArrayElement)(jarray array, int index, int type); - JniResult (*callMethod)(jobject obj, - jmethodID methodID, - int callType, - jvalue* args); - JniResult (*callStaticMethod)(jclass cls, - jmethodID methodID, - int callType, - jvalue* args); - JniResult (*getField)(jobject obj, jfieldID fieldID, int callType); - JniResult (*getStaticField)(jclass cls, jfieldID fieldID, int callType); - JniExceptionDetails (*getExceptionDetails)(jthrowable exception); -} JniAccessorsStruct; - -FFI_PLUGIN_EXPORT JniAccessorsStruct* GetAccessors(); - -FFI_PLUGIN_EXPORT JavaVM* GetJavaVM(void); - -FFI_PLUGIN_EXPORT JNIEnv* GetJniEnv(void); - -/// Spawn a JVM with given arguments. -/// -/// Returns JNI_OK on success, and one of the documented JNI error codes on -/// failure. It returns DART_JNI_SINGLETON_EXISTS if an attempt to spawn multiple -/// JVMs is made, even if the underlying API potentially supports multiple VMs. -FFI_PLUGIN_EXPORT int SpawnJvm(JavaVMInitArgs* args); - -/// Load class through platform-specific mechanism. -/// -/// Currently uses application classloader on android, -/// and JNIEnv->FindClass on other platforms. -FFI_PLUGIN_EXPORT jclass FindClass(const char* name); - -/// Returns Application classLoader (on Android), -/// which can be used to load application and platform classes. -/// -/// On other platforms, NULL is returned. -FFI_PLUGIN_EXPORT jobject GetClassLoader(void); - -/// Returns application context on Android. -/// -/// On other platforms, NULL is returned. -FFI_PLUGIN_EXPORT jobject GetApplicationContext(void); - -/// Returns current activity of the app on Android. -FFI_PLUGIN_EXPORT jobject GetCurrentActivity(void); - -static inline void attach_thread() { - if (jniEnv == NULL) { - (*jni->jvm)->AttachCurrentThread(jni->jvm, __ENVP_CAST & jniEnv, NULL); - } -} - -/// Load class into [cls] using platform specific mechanism -static inline void load_class_platform(jclass* cls, const char* name) { -#ifdef __ANDROID__ - jstring className = (*jniEnv)->NewStringUTF(jniEnv, name); - *cls = (*jniEnv)->CallObjectMethod(jniEnv, jni->classLoader, - jni->loadClassMethod, className); - (*jniEnv)->DeleteLocalRef(jniEnv, className); -#else - *cls = (*jniEnv)->FindClass(jniEnv, name); -#endif -} - -static inline void load_class_local_ref(jclass* cls, const char* name) { - if (*cls == NULL) { - acquire_lock(&jni->locks.classLoadingLock); - if (*cls == NULL) { - load_class_platform(cls, name); - } - release_lock(&jni->locks.classLoadingLock); - } -} - -static inline void load_class_global_ref(jclass* cls, const char* name) { - if (*cls == NULL) { - jclass tmp = NULL; - acquire_lock(&jni->locks.classLoadingLock); - if (*cls == NULL) { - load_class_platform(&tmp, name); - if (!(*jniEnv)->ExceptionCheck(jniEnv)) { - *cls = (*jniEnv)->NewGlobalRef(jniEnv, tmp); - (*jniEnv)->DeleteLocalRef(jniEnv, tmp); - } - } - release_lock(&jni->locks.classLoadingLock); - } -} - -static inline void load_method(jclass cls, - jmethodID* res, - const char* name, - const char* sig) { - if (*res == NULL) { - acquire_lock(&jni->locks.methodLoadingLock); - if (*res == NULL) { - *res = (*jniEnv)->GetMethodID(jniEnv, cls, name, sig); - } - release_lock(&jni->locks.methodLoadingLock); - } -} - -static inline void load_static_method(jclass cls, - jmethodID* res, - const char* name, - const char* sig) { - if (*res == NULL) { - acquire_lock(&jni->locks.methodLoadingLock); - if (*res == NULL) { - *res = (*jniEnv)->GetStaticMethodID(jniEnv, cls, name, sig); - } - release_lock(&jni->locks.methodLoadingLock); - } -} - -static inline void load_field(jclass cls, - jfieldID* res, - const char* name, - const char* sig) { - if (*res == NULL) { - acquire_lock(&jni->locks.fieldLoadingLock); - if (*res == NULL) { - *res = (*jniEnv)->GetFieldID(jniEnv, cls, name, sig); - } - release_lock(&jni->locks.fieldLoadingLock); - } -} - -static inline void load_static_field(jclass cls, - jfieldID* res, - const char* name, - const char* sig) { - if (*res == NULL) { - acquire_lock(&jni->locks.fieldLoadingLock); - if (*res == NULL) { - *res = (*jniEnv)->GetStaticFieldID(jniEnv, cls, name, sig); - } - release_lock(&jni->locks.fieldLoadingLock); - } -} - -static inline jobject to_global_ref(jobject ref) { - jobject g = (*jniEnv)->NewGlobalRef(jniEnv, ref); - (*jniEnv)->DeleteLocalRef(jniEnv, ref); - return g; -} - -// These functions are useful for C+Dart bindings, and not required for pure dart bindings. - -FFI_PLUGIN_EXPORT JniContext* GetJniContextPtr(); - -/// For use by jni_gen's generated code -/// don't use these. - -// these 2 fn ptr vars will be defined by generated code library -extern JniContext* (*context_getter)(void); -extern JNIEnv* (*env_getter)(void); - -// this function will be exported by generated code library -// it will set above 2 variables. -FFI_PLUGIN_EXPORT void setJniGetters(struct JniContext* (*cg)(void), - JNIEnv* (*eg)(void)); - -static inline void load_env() { - if (jniEnv == NULL) { - jni = context_getter(); - jniEnv = env_getter(); - } -} - -static inline jthrowable check_exception() { - jthrowable exception = (*jniEnv)->ExceptionOccurred(jniEnv); - if (exception != NULL) (*jniEnv)->ExceptionClear(jniEnv); - if (exception == NULL) return NULL; - return to_global_ref(exception); -} - -static inline JniResult to_global_ref_result(jobject ref) { - JniResult result; - result.exception = check_exception(); - if (result.exception == NULL) { - result.value.l = to_global_ref(ref); - } - return result; -} - -FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data); - -FFI_PLUGIN_EXPORT -JniResult DartException__ctor(jstring message); - -FFI_PLUGIN_EXPORT -JniResult PortContinuation__ctor(int64_t j); - -FFI_PLUGIN_EXPORT -JniResult PortProxy__newInstance(jobject binaryName, - int64_t port, - int64_t functionPtr); - -FFI_PLUGIN_EXPORT void resultFor(CallbackResult* result, jobject object); diff --git a/flutter/android/src/main/cpp/sentry_android_binding.c b/flutter/android/src/main/cpp/sentry_android_binding.c deleted file mode 100644 index f610456970..0000000000 --- a/flutter/android/src/main/cpp/sentry_android_binding.c +++ /dev/null @@ -1,1800 +0,0 @@ -// Autogenerated by jnigen. DO NOT EDIT! - -#include -#include "dartjni.h" -#include "jni.h" - -thread_local JNIEnv* jniEnv; -JniContext* jni; - -JniContext* (*context_getter)(void); -JNIEnv* (*env_getter)(void); - -void setJniGetters(JniContext* (*cg)(void), JNIEnv* (*eg)(void)) { - context_getter = cg; - env_getter = eg; -} - -// java.io.File -jclass _c_File = NULL; - -jmethodID _m_File__new0 = NULL; -FFI_PLUGIN_EXPORT -JniResult File__new0(jobject string) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__new0, "", "(Ljava/lang/String;)V"); - if (_m_File__new0 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->NewObject(jniEnv, _c_File, _m_File__new0, string); - return to_global_ref_result(_result); -} - -jmethodID _m_File__new1 = NULL; -FFI_PLUGIN_EXPORT -JniResult File__new1(jobject string, jobject string1) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__new1, "", - "(Ljava/lang/String;Ljava/lang/String;)V"); - if (_m_File__new1 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->NewObject(jniEnv, _c_File, _m_File__new1, string, string1); - return to_global_ref_result(_result); -} - -jmethodID _m_File__new2 = NULL; -FFI_PLUGIN_EXPORT -JniResult File__new2(jobject file, jobject string) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__new2, "", - "(Ljava/io/File;Ljava/lang/String;)V"); - if (_m_File__new2 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->NewObject(jniEnv, _c_File, _m_File__new2, file, string); - return to_global_ref_result(_result); -} - -jmethodID _m_File__new3 = NULL; -FFI_PLUGIN_EXPORT -JniResult File__new3(jobject uRI) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__new3, "", "(Ljava/net/URI;)V"); - if (_m_File__new3 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->NewObject(jniEnv, _c_File, _m_File__new3, uRI); - return to_global_ref_result(_result); -} - -jmethodID _m_File__getName = NULL; -FFI_PLUGIN_EXPORT -JniResult File__getName(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getName, "getName", "()Ljava/lang/String;"); - if (_m_File__getName == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getName); - return to_global_ref_result(_result); -} - -jmethodID _m_File__getParent = NULL; -FFI_PLUGIN_EXPORT -JniResult File__getParent(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getParent, "getParent", - "()Ljava/lang/String;"); - if (_m_File__getParent == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getParent); - return to_global_ref_result(_result); -} - -jmethodID _m_File__getParentFile = NULL; -FFI_PLUGIN_EXPORT -JniResult File__getParentFile(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getParentFile, "getParentFile", - "()Ljava/io/File;"); - if (_m_File__getParentFile == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getParentFile); - return to_global_ref_result(_result); -} - -jmethodID _m_File__getPath = NULL; -FFI_PLUGIN_EXPORT -JniResult File__getPath(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getPath, "getPath", "()Ljava/lang/String;"); - if (_m_File__getPath == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getPath); - return to_global_ref_result(_result); -} - -jmethodID _m_File__isAbsolute = NULL; -FFI_PLUGIN_EXPORT -JniResult File__isAbsolute(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__isAbsolute, "isAbsolute", "()Z"); - if (_m_File__isAbsolute == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__isAbsolute); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__getAbsolutePath = NULL; -FFI_PLUGIN_EXPORT -JniResult File__getAbsolutePath(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getAbsolutePath, "getAbsolutePath", - "()Ljava/lang/String;"); - if (_m_File__getAbsolutePath == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getAbsolutePath); - return to_global_ref_result(_result); -} - -jmethodID _m_File__getAbsoluteFile = NULL; -FFI_PLUGIN_EXPORT -JniResult File__getAbsoluteFile(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getAbsoluteFile, "getAbsoluteFile", - "()Ljava/io/File;"); - if (_m_File__getAbsoluteFile == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getAbsoluteFile); - return to_global_ref_result(_result); -} - -jmethodID _m_File__getCanonicalPath = NULL; -FFI_PLUGIN_EXPORT -JniResult File__getCanonicalPath(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getCanonicalPath, "getCanonicalPath", - "()Ljava/lang/String;"); - if (_m_File__getCanonicalPath == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getCanonicalPath); - return to_global_ref_result(_result); -} - -jmethodID _m_File__getCanonicalFile = NULL; -FFI_PLUGIN_EXPORT -JniResult File__getCanonicalFile(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getCanonicalFile, "getCanonicalFile", - "()Ljava/io/File;"); - if (_m_File__getCanonicalFile == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__getCanonicalFile); - return to_global_ref_result(_result); -} - -jmethodID _m_File__toURL = NULL; -FFI_PLUGIN_EXPORT -JniResult File__toURL(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__toURL, "toURL", "()Ljava/net/URL;"); - if (_m_File__toURL == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__toURL); - return to_global_ref_result(_result); -} - -jmethodID _m_File__toURI = NULL; -FFI_PLUGIN_EXPORT -JniResult File__toURI(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__toURI, "toURI", "()Ljava/net/URI;"); - if (_m_File__toURI == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__toURI); - return to_global_ref_result(_result); -} - -jmethodID _m_File__canRead = NULL; -FFI_PLUGIN_EXPORT -JniResult File__canRead(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__canRead, "canRead", "()Z"); - if (_m_File__canRead == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__canRead); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__canWrite = NULL; -FFI_PLUGIN_EXPORT -JniResult File__canWrite(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__canWrite, "canWrite", "()Z"); - if (_m_File__canWrite == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__canWrite); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__exists = NULL; -FFI_PLUGIN_EXPORT -JniResult File__exists(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__exists, "exists", "()Z"); - if (_m_File__exists == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__exists); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__isDirectory = NULL; -FFI_PLUGIN_EXPORT -JniResult File__isDirectory(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__isDirectory, "isDirectory", "()Z"); - if (_m_File__isDirectory == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__isDirectory); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__isFile = NULL; -FFI_PLUGIN_EXPORT -JniResult File__isFile(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__isFile, "isFile", "()Z"); - if (_m_File__isFile == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__isFile); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__isHidden = NULL; -FFI_PLUGIN_EXPORT -JniResult File__isHidden(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__isHidden, "isHidden", "()Z"); - if (_m_File__isHidden == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__isHidden); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__lastModified = NULL; -FFI_PLUGIN_EXPORT -JniResult File__lastModified(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__lastModified, "lastModified", "()J"); - if (_m_File__lastModified == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int64_t _result = - (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__lastModified); - return (JniResult){.value = {.j = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__length = NULL; -FFI_PLUGIN_EXPORT -JniResult File__length(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__length, "length", "()J"); - if (_m_File__length == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int64_t _result = (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__length); - return (JniResult){.value = {.j = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__createNewFile = NULL; -FFI_PLUGIN_EXPORT -JniResult File__createNewFile(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__createNewFile, "createNewFile", "()Z"); - if (_m_File__createNewFile == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__createNewFile); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__delete = NULL; -FFI_PLUGIN_EXPORT -JniResult File__delete(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__delete, "delete", "()Z"); - if (_m_File__delete == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__delete); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__deleteOnExit = NULL; -FFI_PLUGIN_EXPORT -JniResult File__deleteOnExit(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__deleteOnExit, "deleteOnExit", "()V"); - if (_m_File__deleteOnExit == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_File__deleteOnExit); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jmethodID _m_File__list = NULL; -FFI_PLUGIN_EXPORT -JniResult File__list(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__list, "list", "()[Ljava/lang/String;"); - if (_m_File__list == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__list); - return to_global_ref_result(_result); -} - -jmethodID _m_File__list1 = NULL; -FFI_PLUGIN_EXPORT -JniResult File__list1(jobject self_, jobject filenameFilter) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__list1, "list", - "(Ljava/io/FilenameFilter;)[Ljava/lang/String;"); - if (_m_File__list1 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__list1, - filenameFilter); - return to_global_ref_result(_result); -} - -jmethodID _m_File__listFiles = NULL; -FFI_PLUGIN_EXPORT -JniResult File__listFiles(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__listFiles, "listFiles", "()[Ljava/io/File;"); - if (_m_File__listFiles == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__listFiles); - return to_global_ref_result(_result); -} - -jmethodID _m_File__listFiles1 = NULL; -FFI_PLUGIN_EXPORT -JniResult File__listFiles1(jobject self_, jobject filenameFilter) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__listFiles1, "listFiles", - "(Ljava/io/FilenameFilter;)[Ljava/io/File;"); - if (_m_File__listFiles1 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_File__listFiles1, filenameFilter); - return to_global_ref_result(_result); -} - -jmethodID _m_File__listFiles2 = NULL; -FFI_PLUGIN_EXPORT -JniResult File__listFiles2(jobject self_, jobject fileFilter) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__listFiles2, "listFiles", - "(Ljava/io/FileFilter;)[Ljava/io/File;"); - if (_m_File__listFiles2 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_File__listFiles2, fileFilter); - return to_global_ref_result(_result); -} - -jmethodID _m_File__mkdir = NULL; -FFI_PLUGIN_EXPORT -JniResult File__mkdir(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__mkdir, "mkdir", "()Z"); - if (_m_File__mkdir == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__mkdir); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__mkdirs = NULL; -FFI_PLUGIN_EXPORT -JniResult File__mkdirs(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__mkdirs, "mkdirs", "()Z"); - if (_m_File__mkdirs == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__mkdirs); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__renameTo = NULL; -FFI_PLUGIN_EXPORT -JniResult File__renameTo(jobject self_, jobject file) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__renameTo, "renameTo", "(Ljava/io/File;)Z"); - if (_m_File__renameTo == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__renameTo, file); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__setLastModified = NULL; -FFI_PLUGIN_EXPORT -JniResult File__setLastModified(jobject self_, int64_t j) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__setLastModified, "setLastModified", "(J)Z"); - if (_m_File__setLastModified == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setLastModified, j); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__setReadOnly = NULL; -FFI_PLUGIN_EXPORT -JniResult File__setReadOnly(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__setReadOnly, "setReadOnly", "()Z"); - if (_m_File__setReadOnly == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setReadOnly); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__setWritable = NULL; -FFI_PLUGIN_EXPORT -JniResult File__setWritable(jobject self_, uint8_t z, uint8_t z1) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__setWritable, "setWritable", "(ZZ)Z"); - if (_m_File__setWritable == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setWritable, z, z1); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__setWritable1 = NULL; -FFI_PLUGIN_EXPORT -JniResult File__setWritable1(jobject self_, uint8_t z) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__setWritable1, "setWritable", "(Z)Z"); - if (_m_File__setWritable1 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setWritable1, z); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__setReadable = NULL; -FFI_PLUGIN_EXPORT -JniResult File__setReadable(jobject self_, uint8_t z, uint8_t z1) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__setReadable, "setReadable", "(ZZ)Z"); - if (_m_File__setReadable == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setReadable, z, z1); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__setReadable1 = NULL; -FFI_PLUGIN_EXPORT -JniResult File__setReadable1(jobject self_, uint8_t z) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__setReadable1, "setReadable", "(Z)Z"); - if (_m_File__setReadable1 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setReadable1, z); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__setExecutable = NULL; -FFI_PLUGIN_EXPORT -JniResult File__setExecutable(jobject self_, uint8_t z, uint8_t z1) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__setExecutable, "setExecutable", "(ZZ)Z"); - if (_m_File__setExecutable == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = (*jniEnv)->CallBooleanMethod(jniEnv, self_, - _m_File__setExecutable, z, z1); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__setExecutable1 = NULL; -FFI_PLUGIN_EXPORT -JniResult File__setExecutable1(jobject self_, uint8_t z) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__setExecutable1, "setExecutable", "(Z)Z"); - if (_m_File__setExecutable1 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__setExecutable1, z); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__canExecute = NULL; -FFI_PLUGIN_EXPORT -JniResult File__canExecute(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__canExecute, "canExecute", "()Z"); - if (_m_File__canExecute == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__canExecute); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__listRoots = NULL; -FFI_PLUGIN_EXPORT -JniResult File__listRoots() { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_method(_c_File, &_m_File__listRoots, "listRoots", - "()[Ljava/io/File;"); - if (_m_File__listRoots == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->CallStaticObjectMethod(jniEnv, _c_File, _m_File__listRoots); - return to_global_ref_result(_result); -} - -jmethodID _m_File__getTotalSpace = NULL; -FFI_PLUGIN_EXPORT -JniResult File__getTotalSpace(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getTotalSpace, "getTotalSpace", "()J"); - if (_m_File__getTotalSpace == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int64_t _result = - (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__getTotalSpace); - return (JniResult){.value = {.j = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__getFreeSpace = NULL; -FFI_PLUGIN_EXPORT -JniResult File__getFreeSpace(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getFreeSpace, "getFreeSpace", "()J"); - if (_m_File__getFreeSpace == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int64_t _result = - (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__getFreeSpace); - return (JniResult){.value = {.j = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__getUsableSpace = NULL; -FFI_PLUGIN_EXPORT -JniResult File__getUsableSpace(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__getUsableSpace, "getUsableSpace", "()J"); - if (_m_File__getUsableSpace == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int64_t _result = - (*jniEnv)->CallLongMethod(jniEnv, self_, _m_File__getUsableSpace); - return (JniResult){.value = {.j = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__createTempFile = NULL; -FFI_PLUGIN_EXPORT -JniResult File__createTempFile(jobject string, jobject string1, jobject file) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_method( - _c_File, &_m_File__createTempFile, "createTempFile", - "(Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)Ljava/io/File;"); - if (_m_File__createTempFile == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallStaticObjectMethod( - jniEnv, _c_File, _m_File__createTempFile, string, string1, file); - return to_global_ref_result(_result); -} - -jmethodID _m_File__createTempFile1 = NULL; -FFI_PLUGIN_EXPORT -JniResult File__createTempFile1(jobject string, jobject string1) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_method(_c_File, &_m_File__createTempFile1, "createTempFile", - "(Ljava/lang/String;Ljava/lang/String;)Ljava/io/File;"); - if (_m_File__createTempFile1 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallStaticObjectMethod( - jniEnv, _c_File, _m_File__createTempFile1, string, string1); - return to_global_ref_result(_result); -} - -jmethodID _m_File__compareTo = NULL; -FFI_PLUGIN_EXPORT -JniResult File__compareTo(jobject self_, jobject file) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__compareTo, "compareTo", "(Ljava/io/File;)I"); - if (_m_File__compareTo == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = - (*jniEnv)->CallIntMethod(jniEnv, self_, _m_File__compareTo, file); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__equals1 = NULL; -FFI_PLUGIN_EXPORT -JniResult File__equals1(jobject self_, jobject object) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__equals1, "equals", "(Ljava/lang/Object;)Z"); - if (_m_File__equals1 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = - (*jniEnv)->CallBooleanMethod(jniEnv, self_, _m_File__equals1, object); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__hashCode1 = NULL; -FFI_PLUGIN_EXPORT -JniResult File__hashCode1(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__hashCode1, "hashCode", "()I"); - if (_m_File__hashCode1 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod(jniEnv, self_, _m_File__hashCode1); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; -} - -jmethodID _m_File__toString1 = NULL; -FFI_PLUGIN_EXPORT -JniResult File__toString1(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__toString1, "toString", "()Ljava/lang/String;"); - if (_m_File__toString1 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = - (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__toString1); - return to_global_ref_result(_result); -} - -jmethodID _m_File__toPath = NULL; -FFI_PLUGIN_EXPORT -JniResult File__toPath(jobject self_) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__toPath, "toPath", "()Ljava/nio/file/Path;"); - if (_m_File__toPath == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod(jniEnv, self_, _m_File__toPath); - return to_global_ref_result(_result); -} - -jmethodID _m_File__compareTo1 = NULL; -FFI_PLUGIN_EXPORT -JniResult File__compareTo1(jobject self_, jobject object) { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_File, &_m_File__compareTo1, "compareTo", - "(Ljava/lang/Object;)I"); - if (_m_File__compareTo1 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = - (*jniEnv)->CallIntMethod(jniEnv, self_, _m_File__compareTo1, object); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; -} - -jfieldID _f_File__pathSeparator = NULL; -FFI_PLUGIN_EXPORT -JniResult get_File__pathSeparator() { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_File, &_f_File__pathSeparator, "pathSeparator", - "Ljava/lang/String;"); - jobject _result = - (*jniEnv)->GetStaticObjectField(jniEnv, _c_File, _f_File__pathSeparator); - return to_global_ref_result(_result); -} - -jfieldID _f_File__pathSeparatorChar = NULL; -FFI_PLUGIN_EXPORT -JniResult get_File__pathSeparatorChar() { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_File, &_f_File__pathSeparatorChar, "pathSeparatorChar", - "C"); - uint16_t _result = (*jniEnv)->GetStaticCharField(jniEnv, _c_File, - _f_File__pathSeparatorChar); - return (JniResult){.value = {.c = _result}, .exception = check_exception()}; -} - -jfieldID _f_File__separator = NULL; -FFI_PLUGIN_EXPORT -JniResult get_File__separator() { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_File, &_f_File__separator, "separator", - "Ljava/lang/String;"); - jobject _result = - (*jniEnv)->GetStaticObjectField(jniEnv, _c_File, _f_File__separator); - return to_global_ref_result(_result); -} - -jfieldID _f_File__separatorChar = NULL; -FFI_PLUGIN_EXPORT -JniResult get_File__separatorChar() { - load_env(); - load_class_global_ref(&_c_File, "java/io/File"); - if (_c_File == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_File, &_f_File__separatorChar, "separatorChar", "C"); - uint16_t _result = - (*jniEnv)->GetStaticCharField(jniEnv, _c_File, _f_File__separatorChar); - return (JniResult){.value = {.c = _result}, .exception = check_exception()}; -} - -// io.sentry.flutter.SentryFlutterReplay -jclass _c_SentryFlutterReplay = NULL; - -jmethodID _m_SentryFlutterReplay__getIntegration = NULL; -FFI_PLUGIN_EXPORT -JniResult SentryFlutterReplay__getIntegration(jobject self_) { - load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__getIntegration, - "getIntegration", - "()Lio/sentry/android/replay/ReplayIntegration;"); - if (_m_SentryFlutterReplay__getIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_SentryFlutterReplay__getIntegration); - return to_global_ref_result(_result); -} - -jmethodID _m_SentryFlutterReplay__setIntegration = NULL; -FFI_PLUGIN_EXPORT -JniResult SentryFlutterReplay__setIntegration(jobject self_, - jobject replayIntegration) { - load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_SentryFlutterReplay, &_m_SentryFlutterReplay__setIntegration, - "setIntegration", - "(Lio/sentry/android/replay/ReplayIntegration;)V"); - if (_m_SentryFlutterReplay__setIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod( - jniEnv, self_, _m_SentryFlutterReplay__setIntegration, replayIntegration); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jfieldID _f_SentryFlutterReplay__INSTANCE = NULL; -FFI_PLUGIN_EXPORT -JniResult get_SentryFlutterReplay__INSTANCE() { - load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__INSTANCE, - "INSTANCE", "Lio/sentry/flutter/SentryFlutterReplay;"); - jobject _result = (*jniEnv)->GetStaticObjectField( - jniEnv, _c_SentryFlutterReplay, _f_SentryFlutterReplay__INSTANCE); - return to_global_ref_result(_result); -} - -jfieldID _f_SentryFlutterReplay__recorder = NULL; -FFI_PLUGIN_EXPORT -JniResult get_SentryFlutterReplay__recorder() { - load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__recorder, - "recorder", "Lio/sentry/android/replay/Recorder;"); - jobject _result = (*jniEnv)->GetStaticObjectField( - jniEnv, _c_SentryFlutterReplay, _f_SentryFlutterReplay__recorder); - return to_global_ref_result(_result); -} - -FFI_PLUGIN_EXPORT -JniResult set_SentryFlutterReplay__recorder(jobject value) { - load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_SentryFlutterReplay, &_f_SentryFlutterReplay__recorder, - "recorder", "Lio/sentry/android/replay/Recorder;"); - (*jniEnv)->SetStaticObjectField(jniEnv, _c_SentryFlutterReplay, - _f_SentryFlutterReplay__recorder, value); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jfieldID _f_SentryFlutterReplay__integration = NULL; -FFI_PLUGIN_EXPORT -JniResult get_SentryFlutterReplay__integration() { - load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_SentryFlutterReplay, - &_f_SentryFlutterReplay__integration, "integration", - "Lio/sentry/android/replay/ReplayIntegration;"); - jobject _result = (*jniEnv)->GetStaticObjectField( - jniEnv, _c_SentryFlutterReplay, _f_SentryFlutterReplay__integration); - return to_global_ref_result(_result); -} - -FFI_PLUGIN_EXPORT -JniResult set_SentryFlutterReplay__integration(jobject value) { - load_env(); - load_class_global_ref(&_c_SentryFlutterReplay, - "io/sentry/flutter/SentryFlutterReplay"); - if (_c_SentryFlutterReplay == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field(_c_SentryFlutterReplay, - &_f_SentryFlutterReplay__integration, "integration", - "Lio/sentry/android/replay/ReplayIntegration;"); - (*jniEnv)->SetStaticObjectField(jniEnv, _c_SentryFlutterReplay, - _f_SentryFlutterReplay__integration, value); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -// io.sentry.android.replay.Recorder -jclass _c_Recorder = NULL; - -jmethodID _m_Recorder__start = NULL; -FFI_PLUGIN_EXPORT -JniResult Recorder__start(jobject self_, jobject screenshotRecorderConfig) { - load_env(); - load_class_global_ref(&_c_Recorder, "io/sentry/android/replay/Recorder"); - if (_c_Recorder == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_Recorder, &_m_Recorder__start, "start", - "(Lio/sentry/android/replay/ScreenshotRecorderConfig;)V"); - if (_m_Recorder__start == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Recorder__start, - screenshotRecorderConfig); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jmethodID _m_Recorder__resume = NULL; -FFI_PLUGIN_EXPORT -JniResult Recorder__resume(jobject self_) { - load_env(); - load_class_global_ref(&_c_Recorder, "io/sentry/android/replay/Recorder"); - if (_c_Recorder == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_Recorder, &_m_Recorder__resume, "resume", "()V"); - if (_m_Recorder__resume == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Recorder__resume); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jmethodID _m_Recorder__pause = NULL; -FFI_PLUGIN_EXPORT -JniResult Recorder__pause(jobject self_) { - load_env(); - load_class_global_ref(&_c_Recorder, "io/sentry/android/replay/Recorder"); - if (_c_Recorder == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_Recorder, &_m_Recorder__pause, "pause", "()V"); - if (_m_Recorder__pause == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Recorder__pause); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jmethodID _m_Recorder__stop = NULL; -FFI_PLUGIN_EXPORT -JniResult Recorder__stop(jobject self_) { - load_env(); - load_class_global_ref(&_c_Recorder, "io/sentry/android/replay/Recorder"); - if (_c_Recorder == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_Recorder, &_m_Recorder__stop, "stop", "()V"); - if (_m_Recorder__stop == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_Recorder__stop); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -// io.sentry.android.replay.ScreenshotRecorderConfig$Companion -jclass _c_ScreenshotRecorderConfig_Companion = NULL; - -jmethodID _m_ScreenshotRecorderConfig_Companion__from = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig_Companion__from( - jobject self_, - jobject context, - jobject sentryReplayOptions) { - load_env(); - load_class_global_ref( - &_c_ScreenshotRecorderConfig_Companion, - "io/sentry/android/replay/ScreenshotRecorderConfig$Companion"); - if (_c_ScreenshotRecorderConfig_Companion == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig_Companion, - &_m_ScreenshotRecorderConfig_Companion__from, "from", - "(Landroid/content/Context;Lio/sentry/SentryReplayOptions;)Lio/" - "sentry/android/replay/ScreenshotRecorderConfig;"); - if (_m_ScreenshotRecorderConfig_Companion__from == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig_Companion__from, context, - sentryReplayOptions); - return to_global_ref_result(_result); -} - -jmethodID _m_ScreenshotRecorderConfig_Companion__new0 = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig_Companion__new0( - jobject defaultConstructorMarker) { - load_env(); - load_class_global_ref( - &_c_ScreenshotRecorderConfig_Companion, - "io/sentry/android/replay/ScreenshotRecorderConfig$Companion"); - if (_c_ScreenshotRecorderConfig_Companion == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig_Companion, - &_m_ScreenshotRecorderConfig_Companion__new0, "", - "(Lkotlin/jvm/internal/DefaultConstructorMarker;)V"); - if (_m_ScreenshotRecorderConfig_Companion__new0 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->NewObject( - jniEnv, _c_ScreenshotRecorderConfig_Companion, - _m_ScreenshotRecorderConfig_Companion__new0, defaultConstructorMarker); - return to_global_ref_result(_result); -} - -// io.sentry.android.replay.ScreenshotRecorderConfig -jclass _c_ScreenshotRecorderConfig = NULL; - -jmethodID _m_ScreenshotRecorderConfig__new0 = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__new0(int32_t i, - int32_t i1, - float f, - float f1, - int32_t i2, - int32_t i3) { - load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, &_m_ScreenshotRecorderConfig__new0, - "", "(IIFFII)V"); - if (_m_ScreenshotRecorderConfig__new0 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->NewObject(jniEnv, _c_ScreenshotRecorderConfig, - _m_ScreenshotRecorderConfig__new0, i, - i1, f, f1, i2, i3); - return to_global_ref_result(_result); -} - -jmethodID _m_ScreenshotRecorderConfig__getRecordingWidth = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__getRecordingWidth(jobject self_) { - load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__getRecordingWidth, - "getRecordingWidth", "()I"); - if (_m_ScreenshotRecorderConfig__getRecordingWidth == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__getRecordingWidth); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; -} - -jmethodID _m_ScreenshotRecorderConfig__getRecordingHeight = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__getRecordingHeight(jobject self_) { - load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__getRecordingHeight, - "getRecordingHeight", "()I"); - if (_m_ScreenshotRecorderConfig__getRecordingHeight == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__getRecordingHeight); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; -} - -jmethodID _m_ScreenshotRecorderConfig__getScaleFactorX = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__getScaleFactorX(jobject self_) { - load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__getScaleFactorX, "getScaleFactorX", - "()F"); - if (_m_ScreenshotRecorderConfig__getScaleFactorX == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - float _result = (*jniEnv)->CallFloatMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__getScaleFactorX); - return (JniResult){.value = {.f = _result}, .exception = check_exception()}; -} - -jmethodID _m_ScreenshotRecorderConfig__getScaleFactorY = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__getScaleFactorY(jobject self_) { - load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__getScaleFactorY, "getScaleFactorY", - "()F"); - if (_m_ScreenshotRecorderConfig__getScaleFactorY == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - float _result = (*jniEnv)->CallFloatMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__getScaleFactorY); - return (JniResult){.value = {.f = _result}, .exception = check_exception()}; -} - -jmethodID _m_ScreenshotRecorderConfig__getFrameRate = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__getFrameRate(jobject self_) { - load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__getFrameRate, "getFrameRate", - "()I"); - if (_m_ScreenshotRecorderConfig__getFrameRate == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__getFrameRate); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; -} - -jmethodID _m_ScreenshotRecorderConfig__getBitRate = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__getBitRate(jobject self_) { - load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__getBitRate, "getBitRate", "()I"); - if (_m_ScreenshotRecorderConfig__getBitRate == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__getBitRate); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; -} - -jmethodID _m_ScreenshotRecorderConfig__component1 = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__component1(jobject self_) { - load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__component1, "component1", "()I"); - if (_m_ScreenshotRecorderConfig__component1 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__component1); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; -} - -jmethodID _m_ScreenshotRecorderConfig__component2 = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__component2(jobject self_) { - load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__component2, "component2", "()I"); - if (_m_ScreenshotRecorderConfig__component2 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__component2); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; -} - -jmethodID _m_ScreenshotRecorderConfig__component3 = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__component3(jobject self_) { - load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__component3, "component3", "()F"); - if (_m_ScreenshotRecorderConfig__component3 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - float _result = (*jniEnv)->CallFloatMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__component3); - return (JniResult){.value = {.f = _result}, .exception = check_exception()}; -} - -jmethodID _m_ScreenshotRecorderConfig__component4 = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__component4(jobject self_) { - load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__component4, "component4", "()F"); - if (_m_ScreenshotRecorderConfig__component4 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - float _result = (*jniEnv)->CallFloatMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__component4); - return (JniResult){.value = {.f = _result}, .exception = check_exception()}; -} - -jmethodID _m_ScreenshotRecorderConfig__component5 = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__component5(jobject self_) { - load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__component5, "component5", "()I"); - if (_m_ScreenshotRecorderConfig__component5 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__component5); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; -} - -jmethodID _m_ScreenshotRecorderConfig__component6 = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__component6(jobject self_) { - load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__component6, "component6", "()I"); - if (_m_ScreenshotRecorderConfig__component6 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__component6); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; -} - -jmethodID _m_ScreenshotRecorderConfig__copy = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__copy(jobject self_, - int32_t i, - int32_t i1, - float f, - float f1, - int32_t i2, - int32_t i3) { - load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, &_m_ScreenshotRecorderConfig__copy, - "copy", - "(IIFFII)Lio/sentry/android/replay/ScreenshotRecorderConfig;"); - if (_m_ScreenshotRecorderConfig__copy == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__copy, i, i1, f, f1, i2, i3); - return to_global_ref_result(_result); -} - -jmethodID _m_ScreenshotRecorderConfig__toString1 = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__toString1(jobject self_) { - load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__toString1, "toString", - "()Ljava/lang/String;"); - if (_m_ScreenshotRecorderConfig__toString1 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__toString1); - return to_global_ref_result(_result); -} - -jmethodID _m_ScreenshotRecorderConfig__hashCode1 = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__hashCode1(jobject self_) { - load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__hashCode1, "hashCode", "()I"); - if (_m_ScreenshotRecorderConfig__hashCode1 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - int32_t _result = (*jniEnv)->CallIntMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__hashCode1); - return (JniResult){.value = {.i = _result}, .exception = check_exception()}; -} - -jmethodID _m_ScreenshotRecorderConfig__equals1 = NULL; -FFI_PLUGIN_EXPORT -JniResult ScreenshotRecorderConfig__equals1(jobject self_, jobject object) { - load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ScreenshotRecorderConfig, - &_m_ScreenshotRecorderConfig__equals1, "equals", - "(Ljava/lang/Object;)Z"); - if (_m_ScreenshotRecorderConfig__equals1 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = (*jniEnv)->CallBooleanMethod( - jniEnv, self_, _m_ScreenshotRecorderConfig__equals1, object); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jfieldID _f_ScreenshotRecorderConfig__Companion = NULL; -FFI_PLUGIN_EXPORT -JniResult get_ScreenshotRecorderConfig__Companion() { - load_env(); - load_class_global_ref(&_c_ScreenshotRecorderConfig, - "io/sentry/android/replay/ScreenshotRecorderConfig"); - if (_c_ScreenshotRecorderConfig == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_static_field( - _c_ScreenshotRecorderConfig, &_f_ScreenshotRecorderConfig__Companion, - "Companion", - "Lio/sentry/android/replay/ScreenshotRecorderConfig$Companion;"); - jobject _result = - (*jniEnv)->GetStaticObjectField(jniEnv, _c_ScreenshotRecorderConfig, - _f_ScreenshotRecorderConfig__Companion); - return to_global_ref_result(_result); -} - -// io.sentry.android.replay.ReplayIntegration -jclass _c_ReplayIntegration = NULL; - -jmethodID _m_ReplayIntegration__new0 = NULL; -FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__new0(jobject context, - jobject iCurrentDateProvider, - jobject function0, - jobject function1, - jobject function11) { - load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__new0, "", - "(Landroid/content/Context;Lio/sentry/transport/" - "ICurrentDateProvider;Lkotlin/jvm/functions/Function0;Lkotlin/" - "jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V"); - if (_m_ReplayIntegration__new0 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->NewObject( - jniEnv, _c_ReplayIntegration, _m_ReplayIntegration__new0, context, - iCurrentDateProvider, function0, function1, function11); - return to_global_ref_result(_result); -} - -jmethodID _m_ReplayIntegration__new1 = NULL; -FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__new1(jobject context, - jobject iCurrentDateProvider, - jobject function0, - jobject function1, - jobject function11, - int32_t i, - jobject defaultConstructorMarker) { - load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__new1, "", - "(Landroid/content/Context;Lio/sentry/transport/" - "ICurrentDateProvider;Lkotlin/jvm/functions/Function0;Lkotlin/" - "jvm/functions/Function1;Lkotlin/jvm/functions/" - "Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V"); - if (_m_ReplayIntegration__new1 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->NewObject( - jniEnv, _c_ReplayIntegration, _m_ReplayIntegration__new1, context, - iCurrentDateProvider, function0, function1, function11, i, - defaultConstructorMarker); - return to_global_ref_result(_result); -} - -jmethodID _m_ReplayIntegration__new2 = NULL; -FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__new2(jobject context, - jobject iCurrentDateProvider) { - load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method( - _c_ReplayIntegration, &_m_ReplayIntegration__new2, "", - "(Landroid/content/Context;Lio/sentry/transport/ICurrentDateProvider;)V"); - if (_m_ReplayIntegration__new2 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->NewObject(jniEnv, _c_ReplayIntegration, - _m_ReplayIntegration__new2, context, - iCurrentDateProvider); - return to_global_ref_result(_result); -} - -jmethodID _m_ReplayIntegration__getReplayCacheDir = NULL; -FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__getReplayCacheDir(jobject self_) { - load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__getReplayCacheDir, - "getReplayCacheDir", "()Ljava/io/File;"); - if (_m_ReplayIntegration__getReplayCacheDir == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_ReplayIntegration__getReplayCacheDir); - return to_global_ref_result(_result); -} - -jmethodID _m_ReplayIntegration__register = NULL; -FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__register(jobject self_, - jobject iHub, - jobject sentryOptions) { - load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__register, "register", - "(Lio/sentry/IHub;Lio/sentry/SentryOptions;)V"); - if (_m_ReplayIntegration__register == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__register, iHub, - sentryOptions); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jmethodID _m_ReplayIntegration__isRecording = NULL; -FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__isRecording(jobject self_) { - load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__isRecording, - "isRecording", "()Z"); - if (_m_ReplayIntegration__isRecording == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - uint8_t _result = (*jniEnv)->CallBooleanMethod( - jniEnv, self_, _m_ReplayIntegration__isRecording); - return (JniResult){.value = {.z = _result}, .exception = check_exception()}; -} - -jmethodID _m_ReplayIntegration__start = NULL; -FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__start(jobject self_) { - load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__start, "start", - "()V"); - if (_m_ReplayIntegration__start == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__start); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jmethodID _m_ReplayIntegration__resume = NULL; -FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__resume(jobject self_) { - load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__resume, "resume", - "()V"); - if (_m_ReplayIntegration__resume == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__resume); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jmethodID _m_ReplayIntegration__sendReplayForEvent = NULL; -FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__sendReplayForEvent(jobject self_, - jobject sentryEvent, - jobject hint) { - load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__sendReplayForEvent, - "sendReplayForEvent", - "(Lio/sentry/SentryEvent;Lio/sentry/Hint;)V"); - if (_m_ReplayIntegration__sendReplayForEvent == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, - _m_ReplayIntegration__sendReplayForEvent, - sentryEvent, hint); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jmethodID _m_ReplayIntegration__sendReplay = NULL; -FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__sendReplay(jobject self_, - jobject boolean, - jobject string, - jobject hint) { - load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__sendReplay, - "sendReplay", - "(Ljava/lang/Boolean;Ljava/lang/String;Lio/sentry/Hint;)V"); - if (_m_ReplayIntegration__sendReplay == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__sendReplay, - boolean, string, hint); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jmethodID _m_ReplayIntegration__getReplayId = NULL; -FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__getReplayId(jobject self_) { - load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__getReplayId, - "getReplayId", "()Lio/sentry/protocol/SentryId;"); - if (_m_ReplayIntegration__getReplayId == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - jobject _result = (*jniEnv)->CallObjectMethod( - jniEnv, self_, _m_ReplayIntegration__getReplayId); - return to_global_ref_result(_result); -} - -jmethodID _m_ReplayIntegration__pause = NULL; -FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__pause(jobject self_) { - load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__pause, "pause", - "()V"); - if (_m_ReplayIntegration__pause == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__pause); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jmethodID _m_ReplayIntegration__stop = NULL; -FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__stop(jobject self_) { - load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__stop, "stop", "()V"); - if (_m_ReplayIntegration__stop == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__stop); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jmethodID _m_ReplayIntegration__onScreenshotRecorded = NULL; -FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__onScreenshotRecorded(jobject self_, - jobject bitmap) { - load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__onScreenshotRecorded, - "onScreenshotRecorded", "(Landroid/graphics/Bitmap;)V"); - if (_m_ReplayIntegration__onScreenshotRecorded == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, - _m_ReplayIntegration__onScreenshotRecorded, bitmap); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jmethodID _m_ReplayIntegration__onScreenshotRecorded1 = NULL; -FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__onScreenshotRecorded1(jobject self_, - jobject file, - int64_t j) { - load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, - &_m_ReplayIntegration__onScreenshotRecorded1, - "onScreenshotRecorded", "(Ljava/io/File;J)V"); - if (_m_ReplayIntegration__onScreenshotRecorded1 == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod( - jniEnv, self_, _m_ReplayIntegration__onScreenshotRecorded1, file, j); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jmethodID _m_ReplayIntegration__close = NULL; -FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__close(jobject self_) { - load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__close, "close", - "()V"); - if (_m_ReplayIntegration__close == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__close); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jmethodID _m_ReplayIntegration__onConfigurationChanged = NULL; -FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__onConfigurationChanged(jobject self_, - jobject configuration) { - load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method( - _c_ReplayIntegration, &_m_ReplayIntegration__onConfigurationChanged, - "onConfigurationChanged", "(Landroid/content/res/Configuration;)V"); - if (_m_ReplayIntegration__onConfigurationChanged == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, - _m_ReplayIntegration__onConfigurationChanged, - configuration); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} - -jmethodID _m_ReplayIntegration__onLowMemory = NULL; -FFI_PLUGIN_EXPORT -JniResult ReplayIntegration__onLowMemory(jobject self_) { - load_env(); - load_class_global_ref(&_c_ReplayIntegration, - "io/sentry/android/replay/ReplayIntegration"); - if (_c_ReplayIntegration == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - load_method(_c_ReplayIntegration, &_m_ReplayIntegration__onLowMemory, - "onLowMemory", "()V"); - if (_m_ReplayIntegration__onLowMemory == NULL) - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; - (*jniEnv)->CallVoidMethod(jniEnv, self_, _m_ReplayIntegration__onLowMemory); - return (JniResult){.value = {.j = 0}, .exception = check_exception()}; -} diff --git a/flutter/ffi-jni.yaml b/flutter/ffi-jni.yaml deleted file mode 100644 index 6527558e0f..0000000000 --- a/flutter/ffi-jni.yaml +++ /dev/null @@ -1,27 +0,0 @@ -android_sdk_config: - add_gradle_deps: true - android_example: 'example/' - -# summarizer: -# backend: asm - -output: - c: - library_name: sentry_android_binding - path: android/src/main/cpp/ - dart: - path: lib/src/native/java/binding.dart - structure: single_file - -log_level: all - -# Make sure to keep this list up to date with android/proguard-rules.pro -classes: - - java.io.File - - io.sentry.flutter.SentryFlutterReplay - - io.sentry.android.replay.Recorder - - io.sentry.android.replay.ScreenshotRecorderConfig - - io.sentry.android.replay.ReplayIntegration - -enable_experiment: - - interface_implementation diff --git a/flutter/lib/src/native/java/binding.dart b/flutter/lib/src/native/java/binding.dart deleted file mode 100644 index 74c4979d8f..0000000000 --- a/flutter/lib/src/native/java/binding.dart +++ /dev/null @@ -1,1802 +0,0 @@ -// Autogenerated by jnigen. DO NOT EDIT! - -// ignore_for_file: annotate_overrides -// ignore_for_file: camel_case_extensions -// ignore_for_file: camel_case_types -// ignore_for_file: constant_identifier_names -// ignore_for_file: file_names -// ignore_for_file: no_leading_underscores_for_local_identifiers -// ignore_for_file: non_constant_identifier_names -// ignore_for_file: overridden_fields -// ignore_for_file: unnecessary_cast -// ignore_for_file: unused_element -// ignore_for_file: unused_field -// ignore_for_file: unused_import -// ignore_for_file: unused_local_variable -// ignore_for_file: unused_shown_name - -import "dart:isolate" show ReceivePort; -import "dart:ffi" as ffi; -import "package:jni/internal_helpers_for_jnigen.dart"; -import "package:jni/jni.dart" as jni; - -// Auto-generated initialization code. - -final ffi.Pointer Function(String sym) jniLookup = - ProtectedJniExtensions.initGeneratedLibrary("sentry_android_binding"); - -/// from: java.io.File -class File extends jni.JObject { - @override - late final jni.JObjType $type = type; - - File.fromRef( - jni.JObjectPtr ref, - ) : super.fromRef(ref); - - /// The type which includes information such as the signature of this class. - static const type = $FileType(); - static final _get_pathSeparator = - jniLookup>( - "get_File__pathSeparator") - .asFunction(); - - /// from: static public final java.lang.String pathSeparator - /// The returned object must be released after use, by calling the [release] method. - static jni.JString get pathSeparator => - const jni.JStringType().fromRef(_get_pathSeparator().object); - - static final _get_pathSeparatorChar = - jniLookup>( - "get_File__pathSeparatorChar") - .asFunction(); - - /// from: static public final char pathSeparatorChar - static int get pathSeparatorChar => _get_pathSeparatorChar().char; - - static final _get_separator = - jniLookup>( - "get_File__separator") - .asFunction(); - - /// from: static public final java.lang.String separator - /// The returned object must be released after use, by calling the [release] method. - static jni.JString get separator => - const jni.JStringType().fromRef(_get_separator().object); - - static final _get_separatorChar = - jniLookup>( - "get_File__separatorChar") - .asFunction(); - - /// from: static public final char separatorChar - static int get separatorChar => _get_separatorChar().char; - - static final _new0 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__new0") - .asFunction)>(); - - /// from: public void (java.lang.String string) - /// The returned object must be released after use, by calling the [release] method. - factory File( - jni.JString string, - ) { - return File.fromRef(_new0(string.reference).object); - } - - static final _new1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>>("File__new1") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public void (java.lang.String string, java.lang.String string1) - /// The returned object must be released after use, by calling the [release] method. - factory File.new1( - jni.JString string, - jni.JString string1, - ) { - return File.fromRef(_new1(string.reference, string1.reference).object); - } - - static final _new2 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>>("File__new2") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public void (java.io.File file, java.lang.String string) - /// The returned object must be released after use, by calling the [release] method. - factory File.new2( - File file, - jni.JString string, - ) { - return File.fromRef(_new2(file.reference, string.reference).object); - } - - static final _new3 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__new3") - .asFunction)>(); - - /// from: public void (java.net.URI uRI) - /// The returned object must be released after use, by calling the [release] method. - factory File.new3( - jni.JObject uRI, - ) { - return File.fromRef(_new3(uRI.reference).object); - } - - static final _getName = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__getName") - .asFunction)>(); - - /// from: public java.lang.String getName() - /// The returned object must be released after use, by calling the [release] method. - jni.JString getName() { - return const jni.JStringType().fromRef(_getName(reference).object); - } - - static final _getParent = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__getParent") - .asFunction)>(); - - /// from: public java.lang.String getParent() - /// The returned object must be released after use, by calling the [release] method. - jni.JString getParent() { - return const jni.JStringType().fromRef(_getParent(reference).object); - } - - static final _getParentFile = jniLookup< - ffi - .NativeFunction)>>( - "File__getParentFile") - .asFunction)>(); - - /// from: public java.io.File getParentFile() - /// The returned object must be released after use, by calling the [release] method. - File getParentFile() { - return const $FileType().fromRef(_getParentFile(reference).object); - } - - static final _getPath = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__getPath") - .asFunction)>(); - - /// from: public java.lang.String getPath() - /// The returned object must be released after use, by calling the [release] method. - jni.JString getPath() { - return const jni.JStringType().fromRef(_getPath(reference).object); - } - - static final _isAbsolute = jniLookup< - ffi - .NativeFunction)>>( - "File__isAbsolute") - .asFunction)>(); - - /// from: public boolean isAbsolute() - bool isAbsolute() { - return _isAbsolute(reference).boolean; - } - - static final _getAbsolutePath = jniLookup< - ffi - .NativeFunction)>>( - "File__getAbsolutePath") - .asFunction)>(); - - /// from: public java.lang.String getAbsolutePath() - /// The returned object must be released after use, by calling the [release] method. - jni.JString getAbsolutePath() { - return const jni.JStringType().fromRef(_getAbsolutePath(reference).object); - } - - static final _getAbsoluteFile = jniLookup< - ffi - .NativeFunction)>>( - "File__getAbsoluteFile") - .asFunction)>(); - - /// from: public java.io.File getAbsoluteFile() - /// The returned object must be released after use, by calling the [release] method. - File getAbsoluteFile() { - return const $FileType().fromRef(_getAbsoluteFile(reference).object); - } - - static final _getCanonicalPath = jniLookup< - ffi - .NativeFunction)>>( - "File__getCanonicalPath") - .asFunction)>(); - - /// from: public java.lang.String getCanonicalPath() - /// The returned object must be released after use, by calling the [release] method. - jni.JString getCanonicalPath() { - return const jni.JStringType().fromRef(_getCanonicalPath(reference).object); - } - - static final _getCanonicalFile = jniLookup< - ffi - .NativeFunction)>>( - "File__getCanonicalFile") - .asFunction)>(); - - /// from: public java.io.File getCanonicalFile() - /// The returned object must be released after use, by calling the [release] method. - File getCanonicalFile() { - return const $FileType().fromRef(_getCanonicalFile(reference).object); - } - - static final _toURL = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__toURL") - .asFunction)>(); - - /// from: public java.net.URL toURL() - /// The returned object must be released after use, by calling the [release] method. - jni.JObject toURL() { - return const jni.JObjectType().fromRef(_toURL(reference).object); - } - - static final _toURI = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__toURI") - .asFunction)>(); - - /// from: public java.net.URI toURI() - /// The returned object must be released after use, by calling the [release] method. - jni.JObject toURI() { - return const jni.JObjectType().fromRef(_toURI(reference).object); - } - - static final _canRead = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__canRead") - .asFunction)>(); - - /// from: public boolean canRead() - bool canRead() { - return _canRead(reference).boolean; - } - - static final _canWrite = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__canWrite") - .asFunction)>(); - - /// from: public boolean canWrite() - bool canWrite() { - return _canWrite(reference).boolean; - } - - static final _exists = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__exists") - .asFunction)>(); - - /// from: public boolean exists() - bool exists() { - return _exists(reference).boolean; - } - - static final _isDirectory = jniLookup< - ffi - .NativeFunction)>>( - "File__isDirectory") - .asFunction)>(); - - /// from: public boolean isDirectory() - bool isDirectory() { - return _isDirectory(reference).boolean; - } - - static final _isFile = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__isFile") - .asFunction)>(); - - /// from: public boolean isFile() - bool isFile() { - return _isFile(reference).boolean; - } - - static final _isHidden = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__isHidden") - .asFunction)>(); - - /// from: public boolean isHidden() - bool isHidden() { - return _isHidden(reference).boolean; - } - - static final _lastModified = jniLookup< - ffi - .NativeFunction)>>( - "File__lastModified") - .asFunction)>(); - - /// from: public long lastModified() - int lastModified() { - return _lastModified(reference).long; - } - - static final _length = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__length") - .asFunction)>(); - - /// from: public long length() - int length() { - return _length(reference).long; - } - - static final _createNewFile = jniLookup< - ffi - .NativeFunction)>>( - "File__createNewFile") - .asFunction)>(); - - /// from: public boolean createNewFile() - bool createNewFile() { - return _createNewFile(reference).boolean; - } - - static final _delete = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__delete") - .asFunction)>(); - - /// from: public boolean delete() - bool delete() { - return _delete(reference).boolean; - } - - static final _deleteOnExit = jniLookup< - ffi - .NativeFunction)>>( - "File__deleteOnExit") - .asFunction)>(); - - /// from: public void deleteOnExit() - void deleteOnExit() { - return _deleteOnExit(reference).check(); - } - - static final _list = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__list") - .asFunction)>(); - - /// from: public java.lang.String[] list() - /// The returned object must be released after use, by calling the [release] method. - jni.JArray list() { - return const jni.JArrayType(jni.JStringType()) - .fromRef(_list(reference).object); - } - - static final _list1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>>("File__list1") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public java.lang.String[] list(java.io.FilenameFilter filenameFilter) - /// The returned object must be released after use, by calling the [release] method. - jni.JArray list1( - jni.JObject filenameFilter, - ) { - return const jni.JArrayType(jni.JStringType()) - .fromRef(_list1(reference, filenameFilter.reference).object); - } - - static final _listFiles = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__listFiles") - .asFunction)>(); - - /// from: public java.io.File[] listFiles() - /// The returned object must be released after use, by calling the [release] method. - jni.JArray listFiles() { - return const jni.JArrayType($FileType()) - .fromRef(_listFiles(reference).object); - } - - static final _listFiles1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("File__listFiles1") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public java.io.File[] listFiles(java.io.FilenameFilter filenameFilter) - /// The returned object must be released after use, by calling the [release] method. - jni.JArray listFiles1( - jni.JObject filenameFilter, - ) { - return const jni.JArrayType($FileType()) - .fromRef(_listFiles1(reference, filenameFilter.reference).object); - } - - static final _listFiles2 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("File__listFiles2") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public java.io.File[] listFiles(java.io.FileFilter fileFilter) - /// The returned object must be released after use, by calling the [release] method. - jni.JArray listFiles2( - jni.JObject fileFilter, - ) { - return const jni.JArrayType($FileType()) - .fromRef(_listFiles2(reference, fileFilter.reference).object); - } - - static final _mkdir = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__mkdir") - .asFunction)>(); - - /// from: public boolean mkdir() - bool mkdir() { - return _mkdir(reference).boolean; - } - - static final _mkdirs = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__mkdirs") - .asFunction)>(); - - /// from: public boolean mkdirs() - bool mkdirs() { - return _mkdirs(reference).boolean; - } - - static final _renameTo = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("File__renameTo") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public boolean renameTo(java.io.File file) - bool renameTo( - File file, - ) { - return _renameTo(reference, file.reference).boolean; - } - - static final _setLastModified = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Int64)>>("File__setLastModified") - .asFunction, int)>(); - - /// from: public boolean setLastModified(long j) - bool setLastModified( - int j, - ) { - return _setLastModified(reference, j).boolean; - } - - static final _setReadOnly = jniLookup< - ffi - .NativeFunction)>>( - "File__setReadOnly") - .asFunction)>(); - - /// from: public boolean setReadOnly() - bool setReadOnly() { - return _setReadOnly(reference).boolean; - } - - static final _setWritable = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, ffi.Uint8, - ffi.Uint8)>>("File__setWritable") - .asFunction, int, int)>(); - - /// from: public boolean setWritable(boolean z, boolean z1) - bool setWritable( - bool z, - bool z1, - ) { - return _setWritable(reference, z ? 1 : 0, z1 ? 1 : 0).boolean; - } - - static final _setWritable1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Uint8)>>("File__setWritable1") - .asFunction, int)>(); - - /// from: public boolean setWritable(boolean z) - bool setWritable1( - bool z, - ) { - return _setWritable1(reference, z ? 1 : 0).boolean; - } - - static final _setReadable = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, ffi.Uint8, - ffi.Uint8)>>("File__setReadable") - .asFunction, int, int)>(); - - /// from: public boolean setReadable(boolean z, boolean z1) - bool setReadable( - bool z, - bool z1, - ) { - return _setReadable(reference, z ? 1 : 0, z1 ? 1 : 0).boolean; - } - - static final _setReadable1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Uint8)>>("File__setReadable1") - .asFunction, int)>(); - - /// from: public boolean setReadable(boolean z) - bool setReadable1( - bool z, - ) { - return _setReadable1(reference, z ? 1 : 0).boolean; - } - - static final _setExecutable = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, ffi.Uint8, - ffi.Uint8)>>("File__setExecutable") - .asFunction, int, int)>(); - - /// from: public boolean setExecutable(boolean z, boolean z1) - bool setExecutable( - bool z, - bool z1, - ) { - return _setExecutable(reference, z ? 1 : 0, z1 ? 1 : 0).boolean; - } - - static final _setExecutable1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Uint8)>>("File__setExecutable1") - .asFunction, int)>(); - - /// from: public boolean setExecutable(boolean z) - bool setExecutable1( - bool z, - ) { - return _setExecutable1(reference, z ? 1 : 0).boolean; - } - - static final _canExecute = jniLookup< - ffi - .NativeFunction)>>( - "File__canExecute") - .asFunction)>(); - - /// from: public boolean canExecute() - bool canExecute() { - return _canExecute(reference).boolean; - } - - static final _listRoots = - jniLookup>("File__listRoots") - .asFunction(); - - /// from: static public java.io.File[] listRoots() - /// The returned object must be released after use, by calling the [release] method. - static jni.JArray listRoots() { - return const jni.JArrayType($FileType()).fromRef(_listRoots().object); - } - - static final _getTotalSpace = jniLookup< - ffi - .NativeFunction)>>( - "File__getTotalSpace") - .asFunction)>(); - - /// from: public long getTotalSpace() - int getTotalSpace() { - return _getTotalSpace(reference).long; - } - - static final _getFreeSpace = jniLookup< - ffi - .NativeFunction)>>( - "File__getFreeSpace") - .asFunction)>(); - - /// from: public long getFreeSpace() - int getFreeSpace() { - return _getFreeSpace(reference).long; - } - - static final _getUsableSpace = jniLookup< - ffi - .NativeFunction)>>( - "File__getUsableSpace") - .asFunction)>(); - - /// from: public long getUsableSpace() - int getUsableSpace() { - return _getUsableSpace(reference).long; - } - - static final _createTempFile = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer)>>("File__createTempFile") - .asFunction< - jni.JniResult Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer)>(); - - /// from: static public java.io.File createTempFile(java.lang.String string, java.lang.String string1, java.io.File file) - /// The returned object must be released after use, by calling the [release] method. - static File createTempFile( - jni.JString string, - jni.JString string1, - File file, - ) { - return const $FileType().fromRef( - _createTempFile(string.reference, string1.reference, file.reference) - .object); - } - - static final _createTempFile1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("File__createTempFile1") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: static public java.io.File createTempFile(java.lang.String string, java.lang.String string1) - /// The returned object must be released after use, by calling the [release] method. - static File createTempFile1( - jni.JString string, - jni.JString string1, - ) { - return const $FileType() - .fromRef(_createTempFile1(string.reference, string1.reference).object); - } - - static final _compareTo = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("File__compareTo") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public int compareTo(java.io.File file) - int compareTo( - File file, - ) { - return _compareTo(reference, file.reference).integer; - } - - static final _equals1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("File__equals1") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public boolean equals(java.lang.Object object) - bool equals1( - jni.JObject object, - ) { - return _equals1(reference, object.reference).boolean; - } - - static final _hashCode1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__hashCode1") - .asFunction)>(); - - /// from: public int hashCode() - int hashCode1() { - return _hashCode1(reference).integer; - } - - static final _toString1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__toString1") - .asFunction)>(); - - /// from: public java.lang.String toString() - /// The returned object must be released after use, by calling the [release] method. - jni.JString toString1() { - return const jni.JStringType().fromRef(_toString1(reference).object); - } - - static final _toPath = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("File__toPath") - .asFunction)>(); - - /// from: public java.nio.file.Path toPath() - /// The returned object must be released after use, by calling the [release] method. - jni.JObject toPath() { - return const jni.JObjectType().fromRef(_toPath(reference).object); - } - - static final _compareTo1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("File__compareTo1") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public int compareTo(java.lang.Object object) - int compareTo1( - jni.JObject object, - ) { - return _compareTo1(reference, object.reference).integer; - } -} - -class $FileType extends jni.JObjType { - const $FileType(); - - @override - String get signature => r"Ljava/io/File;"; - - @override - File fromRef(jni.JObjectPtr ref) => File.fromRef(ref); - - @override - jni.JObjType get superType => const jni.JObjectType(); - - @override - final superCount = 1; - - @override - int get hashCode => ($FileType).hashCode; - - @override - bool operator ==(Object other) { - return other.runtimeType == ($FileType) && other is $FileType; - } -} - -/// from: io.sentry.flutter.SentryFlutterReplay -class SentryFlutterReplay extends jni.JObject { - @override - late final jni.JObjType $type = type; - - SentryFlutterReplay.fromRef( - jni.JObjectPtr ref, - ) : super.fromRef(ref); - - /// The type which includes information such as the signature of this class. - static const type = $SentryFlutterReplayType(); - static final _get_INSTANCE = - jniLookup>( - "get_SentryFlutterReplay__INSTANCE") - .asFunction(); - - /// from: static public final io.sentry.flutter.SentryFlutterReplay INSTANCE - /// The returned object must be released after use, by calling the [release] method. - static SentryFlutterReplay get INSTANCE => - const $SentryFlutterReplayType().fromRef(_get_INSTANCE().object); - - static final _get_recorder = - jniLookup>( - "get_SentryFlutterReplay__recorder") - .asFunction(); - - static final _set_recorder = jniLookup< - ffi - .NativeFunction)>>( - "set_SentryFlutterReplay__recorder") - .asFunction)>(); - - /// from: static public io.sentry.android.replay.Recorder recorder - /// The returned object must be released after use, by calling the [release] method. - static Recorder get recorder => - const $RecorderType().fromRef(_get_recorder().object); - - /// from: static public io.sentry.android.replay.Recorder recorder - /// The returned object must be released after use, by calling the [release] method. - static set recorder(Recorder value) => _set_recorder(value.reference).check(); - - static final _get_integration = - jniLookup>( - "get_SentryFlutterReplay__integration") - .asFunction(); - - static final _set_integration = jniLookup< - ffi - .NativeFunction)>>( - "set_SentryFlutterReplay__integration") - .asFunction)>(); - - /// from: static public io.sentry.android.replay.ReplayIntegration integration - /// The returned object must be released after use, by calling the [release] method. - static ReplayIntegration get integration => - const $ReplayIntegrationType().fromRef(_get_integration().object); - - /// from: static public io.sentry.android.replay.ReplayIntegration integration - /// The returned object must be released after use, by calling the [release] method. - static set integration(ReplayIntegration value) => - _set_integration(value.reference).check(); - - static final _getIntegration = jniLookup< - ffi - .NativeFunction)>>( - "SentryFlutterReplay__getIntegration") - .asFunction)>(); - - /// from: public final io.sentry.android.replay.ReplayIntegration getIntegration() - /// The returned object must be released after use, by calling the [release] method. - ReplayIntegration getIntegration() { - return const $ReplayIntegrationType() - .fromRef(_getIntegration(reference).object); - } - - static final _setIntegration = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>>( - "SentryFlutterReplay__setIntegration") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public final void setIntegration(io.sentry.android.replay.ReplayIntegration replayIntegration) - void setIntegration( - ReplayIntegration replayIntegration, - ) { - return _setIntegration(reference, replayIntegration.reference).check(); - } -} - -class $SentryFlutterReplayType extends jni.JObjType { - const $SentryFlutterReplayType(); - - @override - String get signature => r"Lio/sentry/flutter/SentryFlutterReplay;"; - - @override - SentryFlutterReplay fromRef(jni.JObjectPtr ref) => - SentryFlutterReplay.fromRef(ref); - - @override - jni.JObjType get superType => const jni.JObjectType(); - - @override - final superCount = 1; - - @override - int get hashCode => ($SentryFlutterReplayType).hashCode; - - @override - bool operator ==(Object other) { - return other.runtimeType == ($SentryFlutterReplayType) && - other is $SentryFlutterReplayType; - } -} - -/// from: io.sentry.android.replay.Recorder -class Recorder extends jni.JObject { - @override - late final jni.JObjType $type = type; - - Recorder.fromRef( - jni.JObjectPtr ref, - ) : super.fromRef(ref); - - /// The type which includes information such as the signature of this class. - static const type = $RecorderType(); - static final _start = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("Recorder__start") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public abstract void start(io.sentry.android.replay.ScreenshotRecorderConfig screenshotRecorderConfig) - void start( - ScreenshotRecorderConfig screenshotRecorderConfig, - ) { - return _start(reference, screenshotRecorderConfig.reference).check(); - } - - static final _resume = jniLookup< - ffi - .NativeFunction)>>( - "Recorder__resume") - .asFunction)>(); - - /// from: public abstract void resume() - void resume() { - return _resume(reference).check(); - } - - static final _pause = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("Recorder__pause") - .asFunction)>(); - - /// from: public abstract void pause() - void pause() { - return _pause(reference).check(); - } - - static final _stop = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer)>>("Recorder__stop") - .asFunction)>(); - - /// from: public abstract void stop() - void stop() { - return _stop(reference).check(); - } - - /// Maps a specific port to the implemented interface. - static final Map _$impls = {}; - ReceivePort? _$p; - - static jni.JObjectPtr _$invoke( - int port, - jni.JObjectPtr descriptor, - jni.JObjectPtr args, - ) { - return _$invokeMethod( - port, - $MethodInvocation.fromAddresses( - 0, - descriptor.address, - args.address, - ), - ); - } - - static final ffi.Pointer< - ffi.NativeFunction< - jni.JObjectPtr Function( - ffi.Uint64, jni.JObjectPtr, jni.JObjectPtr)>> - _$invokePointer = ffi.Pointer.fromFunction(_$invoke); - - static ffi.Pointer _$invokeMethod( - int $p, - $MethodInvocation $i, - ) { - try { - final $d = $i.methodDescriptor.toDartString(releaseOriginal: true); - final $a = $i.args; - if ($d == - r"start(Lio/sentry/android/replay/ScreenshotRecorderConfig;)V") { - _$impls[$p]!.start( - $a[0].castTo(const $ScreenshotRecorderConfigType(), - releaseOriginal: true), - ); - return jni.nullptr; - } - if ($d == r"resume()V") { - _$impls[$p]!.resume(); - return jni.nullptr; - } - if ($d == r"pause()V") { - _$impls[$p]!.pause(); - return jni.nullptr; - } - if ($d == r"stop()V") { - _$impls[$p]!.stop(); - return jni.nullptr; - } - } catch (e) { - return ProtectedJniExtensions.newDartException(e.toString()); - } - return jni.nullptr; - } - - factory Recorder.implement( - $RecorderImpl $impl, - ) { - final $p = ReceivePort(); - final $x = Recorder.fromRef( - ProtectedJniExtensions.newPortProxy( - r"io.sentry.android.replay.Recorder", - $p, - _$invokePointer, - ), - ).._$p = $p; - final $a = $p.sendPort.nativePort; - _$impls[$a] = $impl; - $p.listen(($m) { - if ($m == null) { - _$impls.remove($p.sendPort.nativePort); - $p.close(); - return; - } - final $i = $MethodInvocation.fromMessage($m); - final $r = _$invokeMethod($p.sendPort.nativePort, $i); - ProtectedJniExtensions.returnResult($i.result, $r); - }); - return $x; - } -} - -abstract class $RecorderImpl { - factory $RecorderImpl({ - required void Function(ScreenshotRecorderConfig screenshotRecorderConfig) - start, - required void Function() resume, - required void Function() pause, - required void Function() stop, - }) = _$RecorderImpl; - - void start(ScreenshotRecorderConfig screenshotRecorderConfig); - void resume(); - void pause(); - void stop(); -} - -class _$RecorderImpl implements $RecorderImpl { - _$RecorderImpl({ - required void Function(ScreenshotRecorderConfig screenshotRecorderConfig) - start, - required void Function() resume, - required void Function() pause, - required void Function() stop, - }) : _start = start, - _resume = resume, - _pause = pause, - _stop = stop; - - final void Function(ScreenshotRecorderConfig screenshotRecorderConfig) _start; - final void Function() _resume; - final void Function() _pause; - final void Function() _stop; - - void start(ScreenshotRecorderConfig screenshotRecorderConfig) { - return _start(screenshotRecorderConfig); - } - - void resume() { - return _resume(); - } - - void pause() { - return _pause(); - } - - void stop() { - return _stop(); - } -} - -class $RecorderType extends jni.JObjType { - const $RecorderType(); - - @override - String get signature => r"Lio/sentry/android/replay/Recorder;"; - - @override - Recorder fromRef(jni.JObjectPtr ref) => Recorder.fromRef(ref); - - @override - jni.JObjType get superType => const jni.JObjectType(); - - @override - final superCount = 1; - - @override - int get hashCode => ($RecorderType).hashCode; - - @override - bool operator ==(Object other) { - return other.runtimeType == ($RecorderType) && other is $RecorderType; - } -} - -/// from: io.sentry.android.replay.ScreenshotRecorderConfig$Companion -class ScreenshotRecorderConfig_Companion extends jni.JObject { - @override - late final jni.JObjType $type = type; - - ScreenshotRecorderConfig_Companion.fromRef( - jni.JObjectPtr ref, - ) : super.fromRef(ref); - - /// The type which includes information such as the signature of this class. - static const type = $ScreenshotRecorderConfig_CompanionType(); - static final _from = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer, ffi.Pointer)>>( - "ScreenshotRecorderConfig_Companion__from") - .asFunction< - jni.JniResult Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer)>(); - - /// from: public final io.sentry.android.replay.ScreenshotRecorderConfig from(android.content.Context context, io.sentry.SentryReplayOptions sentryReplayOptions) - /// The returned object must be released after use, by calling the [release] method. - ScreenshotRecorderConfig from( - jni.JObject context, - jni.JObject sentryReplayOptions, - ) { - return const $ScreenshotRecorderConfigType().fromRef( - _from(reference, context.reference, sentryReplayOptions.reference) - .object); - } - - static final _new0 = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig_Companion__new0") - .asFunction)>(); - - /// from: public void (kotlin.jvm.internal.DefaultConstructorMarker defaultConstructorMarker) - /// The returned object must be released after use, by calling the [release] method. - factory ScreenshotRecorderConfig_Companion( - jni.JObject defaultConstructorMarker, - ) { - return ScreenshotRecorderConfig_Companion.fromRef( - _new0(defaultConstructorMarker.reference).object); - } -} - -class $ScreenshotRecorderConfig_CompanionType - extends jni.JObjType { - const $ScreenshotRecorderConfig_CompanionType(); - - @override - String get signature => - r"Lio/sentry/android/replay/ScreenshotRecorderConfig$Companion;"; - - @override - ScreenshotRecorderConfig_Companion fromRef(jni.JObjectPtr ref) => - ScreenshotRecorderConfig_Companion.fromRef(ref); - - @override - jni.JObjType get superType => const jni.JObjectType(); - - @override - final superCount = 1; - - @override - int get hashCode => ($ScreenshotRecorderConfig_CompanionType).hashCode; - - @override - bool operator ==(Object other) { - return other.runtimeType == ($ScreenshotRecorderConfig_CompanionType) && - other is $ScreenshotRecorderConfig_CompanionType; - } -} - -/// from: io.sentry.android.replay.ScreenshotRecorderConfig -class ScreenshotRecorderConfig extends jni.JObject { - @override - late final jni.JObjType $type = type; - - ScreenshotRecorderConfig.fromRef( - jni.JObjectPtr ref, - ) : super.fromRef(ref); - - /// The type which includes information such as the signature of this class. - static const type = $ScreenshotRecorderConfigType(); - static final _get_Companion = - jniLookup>( - "get_ScreenshotRecorderConfig__Companion") - .asFunction(); - - /// from: static public final io.sentry.android.replay.ScreenshotRecorderConfig$Companion Companion - /// The returned object must be released after use, by calling the [release] method. - static ScreenshotRecorderConfig_Companion get Companion => - const $ScreenshotRecorderConfig_CompanionType() - .fromRef(_get_Companion().object); - - static final _new0 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Int32, ffi.Int32, ffi.Float, ffi.Float, - ffi.Int32, ffi.Int32)>>("ScreenshotRecorderConfig__new0") - .asFunction(); - - /// from: public void (int i, int i1, float f, float f1, int i2, int i3) - /// The returned object must be released after use, by calling the [release] method. - factory ScreenshotRecorderConfig( - int i, - int i1, - double f, - double f1, - int i2, - int i3, - ) { - return ScreenshotRecorderConfig.fromRef(_new0(i, i1, f, f1, i2, i3).object); - } - - static final _getRecordingWidth = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__getRecordingWidth") - .asFunction)>(); - - /// from: public final int getRecordingWidth() - int getRecordingWidth() { - return _getRecordingWidth(reference).integer; - } - - static final _getRecordingHeight = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__getRecordingHeight") - .asFunction)>(); - - /// from: public final int getRecordingHeight() - int getRecordingHeight() { - return _getRecordingHeight(reference).integer; - } - - static final _getScaleFactorX = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__getScaleFactorX") - .asFunction)>(); - - /// from: public final float getScaleFactorX() - double getScaleFactorX() { - return _getScaleFactorX(reference).float; - } - - static final _getScaleFactorY = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__getScaleFactorY") - .asFunction)>(); - - /// from: public final float getScaleFactorY() - double getScaleFactorY() { - return _getScaleFactorY(reference).float; - } - - static final _getFrameRate = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__getFrameRate") - .asFunction)>(); - - /// from: public final int getFrameRate() - int getFrameRate() { - return _getFrameRate(reference).integer; - } - - static final _getBitRate = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__getBitRate") - .asFunction)>(); - - /// from: public final int getBitRate() - int getBitRate() { - return _getBitRate(reference).integer; - } - - static final _component1 = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__component1") - .asFunction)>(); - - /// from: public final int component1() - int component1() { - return _component1(reference).integer; - } - - static final _component2 = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__component2") - .asFunction)>(); - - /// from: public final int component2() - int component2() { - return _component2(reference).integer; - } - - static final _component3 = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__component3") - .asFunction)>(); - - /// from: public final float component3() - double component3() { - return _component3(reference).float; - } - - static final _component4 = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__component4") - .asFunction)>(); - - /// from: public final float component4() - double component4() { - return _component4(reference).float; - } - - static final _component5 = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__component5") - .asFunction)>(); - - /// from: public final int component5() - int component5() { - return _component5(reference).integer; - } - - static final _component6 = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__component6") - .asFunction)>(); - - /// from: public final int component6() - int component6() { - return _component6(reference).integer; - } - - static final _copy = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, - ffi.Int32, - ffi.Int32, - ffi.Float, - ffi.Float, - ffi.Int32, - ffi.Int32)>>("ScreenshotRecorderConfig__copy") - .asFunction< - jni.JniResult Function( - ffi.Pointer, int, int, double, double, int, int)>(); - - /// from: public final io.sentry.android.replay.ScreenshotRecorderConfig copy(int i, int i1, float f, float f1, int i2, int i3) - /// The returned object must be released after use, by calling the [release] method. - ScreenshotRecorderConfig copy( - int i, - int i1, - double f, - double f1, - int i2, - int i3, - ) { - return const $ScreenshotRecorderConfigType() - .fromRef(_copy(reference, i, i1, f, f1, i2, i3).object); - } - - static final _toString1 = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__toString1") - .asFunction)>(); - - /// from: public java.lang.String toString() - /// The returned object must be released after use, by calling the [release] method. - jni.JString toString1() { - return const jni.JStringType().fromRef(_toString1(reference).object); - } - - static final _hashCode1 = jniLookup< - ffi - .NativeFunction)>>( - "ScreenshotRecorderConfig__hashCode1") - .asFunction)>(); - - /// from: public int hashCode() - int hashCode1() { - return _hashCode1(reference).integer; - } - - static final _equals1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("ScreenshotRecorderConfig__equals1") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public boolean equals(java.lang.Object object) - bool equals1( - jni.JObject object, - ) { - return _equals1(reference, object.reference).boolean; - } -} - -class $ScreenshotRecorderConfigType - extends jni.JObjType { - const $ScreenshotRecorderConfigType(); - - @override - String get signature => - r"Lio/sentry/android/replay/ScreenshotRecorderConfig;"; - - @override - ScreenshotRecorderConfig fromRef(jni.JObjectPtr ref) => - ScreenshotRecorderConfig.fromRef(ref); - - @override - jni.JObjType get superType => const jni.JObjectType(); - - @override - final superCount = 1; - - @override - int get hashCode => ($ScreenshotRecorderConfigType).hashCode; - - @override - bool operator ==(Object other) { - return other.runtimeType == ($ScreenshotRecorderConfigType) && - other is $ScreenshotRecorderConfigType; - } -} - -/// from: io.sentry.android.replay.ReplayIntegration -class ReplayIntegration extends jni.JObject { - @override - late final jni.JObjType $type = type; - - ReplayIntegration.fromRef( - jni.JObjectPtr ref, - ) : super.fromRef(ref); - - /// The type which includes information such as the signature of this class. - static const type = $ReplayIntegrationType(); - static final _new0 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer)>>("ReplayIntegration__new0") - .asFunction< - jni.JniResult Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer)>(); - - /// from: public void (android.content.Context context, io.sentry.transport.ICurrentDateProvider iCurrentDateProvider, kotlin.jvm.functions.Function0 function0, kotlin.jvm.functions.Function1 function1, kotlin.jvm.functions.Function1 function11) - /// The returned object must be released after use, by calling the [release] method. - factory ReplayIntegration( - jni.JObject context, - jni.JObject iCurrentDateProvider, - jni.JObject function0, - jni.JObject function1, - jni.JObject function11, - ) { - return ReplayIntegration.fromRef(_new0( - context.reference, - iCurrentDateProvider.reference, - function0.reference, - function1.reference, - function11.reference) - .object); - } - - static final _new1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Int32, - ffi.Pointer)>>("ReplayIntegration__new1") - .asFunction< - jni.JniResult Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - int, - ffi.Pointer)>(); - - /// from: public void (android.content.Context context, io.sentry.transport.ICurrentDateProvider iCurrentDateProvider, kotlin.jvm.functions.Function0 function0, kotlin.jvm.functions.Function1 function1, kotlin.jvm.functions.Function1 function11, int i, kotlin.jvm.internal.DefaultConstructorMarker defaultConstructorMarker) - /// The returned object must be released after use, by calling the [release] method. - factory ReplayIntegration.new1( - jni.JObject context, - jni.JObject iCurrentDateProvider, - jni.JObject function0, - jni.JObject function1, - jni.JObject function11, - int i, - jni.JObject defaultConstructorMarker, - ) { - return ReplayIntegration.fromRef(_new1( - context.reference, - iCurrentDateProvider.reference, - function0.reference, - function1.reference, - function11.reference, - i, - defaultConstructorMarker.reference) - .object); - } - - static final _new2 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer)>>("ReplayIntegration__new2") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public void (android.content.Context context, io.sentry.transport.ICurrentDateProvider iCurrentDateProvider) - /// The returned object must be released after use, by calling the [release] method. - factory ReplayIntegration.new2( - jni.JObject context, - jni.JObject iCurrentDateProvider, - ) { - return ReplayIntegration.fromRef( - _new2(context.reference, iCurrentDateProvider.reference).object); - } - - static final _getReplayCacheDir = jniLookup< - ffi - .NativeFunction)>>( - "ReplayIntegration__getReplayCacheDir") - .asFunction)>(); - - /// from: public final java.io.File getReplayCacheDir() - /// The returned object must be released after use, by calling the [release] method. - File getReplayCacheDir() { - return const $FileType().fromRef(_getReplayCacheDir(reference).object); - } - - static final _register = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer)>>("ReplayIntegration__register") - .asFunction< - jni.JniResult Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer)>(); - - /// from: public void register(io.sentry.IHub iHub, io.sentry.SentryOptions sentryOptions) - void register( - jni.JObject iHub, - jni.JObject sentryOptions, - ) { - return _register(reference, iHub.reference, sentryOptions.reference) - .check(); - } - - static final _isRecording = jniLookup< - ffi - .NativeFunction)>>( - "ReplayIntegration__isRecording") - .asFunction)>(); - - /// from: public boolean isRecording() - bool isRecording() { - return _isRecording(reference).boolean; - } - - static final _start = jniLookup< - ffi - .NativeFunction)>>( - "ReplayIntegration__start") - .asFunction)>(); - - /// from: public void start() - void start() { - return _start(reference).check(); - } - - static final _resume = jniLookup< - ffi - .NativeFunction)>>( - "ReplayIntegration__resume") - .asFunction)>(); - - /// from: public void resume() - void resume() { - return _resume(reference).check(); - } - - static final _sendReplayForEvent = jniLookup< - ffi.NativeFunction< - jni.JniResult Function(ffi.Pointer, - ffi.Pointer, ffi.Pointer)>>( - "ReplayIntegration__sendReplayForEvent") - .asFunction< - jni.JniResult Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer)>(); - - /// from: public void sendReplayForEvent(io.sentry.SentryEvent sentryEvent, io.sentry.Hint hint) - void sendReplayForEvent( - jni.JObject sentryEvent, - jni.JObject hint, - ) { - return _sendReplayForEvent(reference, sentryEvent.reference, hint.reference) - .check(); - } - - static final _sendReplay = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer)>>("ReplayIntegration__sendReplay") - .asFunction< - jni.JniResult Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer, ffi.Pointer)>(); - - /// from: public void sendReplay(java.lang.Boolean boolean, java.lang.String string, io.sentry.Hint hint) - void sendReplay( - jni.JBoolean boolean, - jni.JString string, - jni.JObject hint, - ) { - return _sendReplay( - reference, boolean.reference, string.reference, hint.reference) - .check(); - } - - static final _getReplayId = jniLookup< - ffi - .NativeFunction)>>( - "ReplayIntegration__getReplayId") - .asFunction)>(); - - /// from: public io.sentry.protocol.SentryId getReplayId() - /// The returned object must be released after use, by calling the [release] method. - jni.JObject getReplayId() { - return const jni.JObjectType().fromRef(_getReplayId(reference).object); - } - - static final _pause = jniLookup< - ffi - .NativeFunction)>>( - "ReplayIntegration__pause") - .asFunction)>(); - - /// from: public void pause() - void pause() { - return _pause(reference).check(); - } - - static final _stop = jniLookup< - ffi - .NativeFunction)>>( - "ReplayIntegration__stop") - .asFunction)>(); - - /// from: public void stop() - void stop() { - return _stop(reference).check(); - } - - static final _onScreenshotRecorded = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>>( - "ReplayIntegration__onScreenshotRecorded") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public void onScreenshotRecorded(android.graphics.Bitmap bitmap) - void onScreenshotRecorded( - jni.JObject bitmap, - ) { - return _onScreenshotRecorded(reference, bitmap.reference).check(); - } - - static final _onScreenshotRecorded1 = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, - ffi.Pointer, - ffi.Int64)>>("ReplayIntegration__onScreenshotRecorded1") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer, int)>(); - - /// from: public void onScreenshotRecorded(java.io.File file, long j) - void onScreenshotRecorded1( - File file, - int j, - ) { - return _onScreenshotRecorded1(reference, file.reference, j).check(); - } - - static final _close = jniLookup< - ffi - .NativeFunction)>>( - "ReplayIntegration__close") - .asFunction)>(); - - /// from: public void close() - void close() { - return _close(reference).check(); - } - - static final _onConfigurationChanged = jniLookup< - ffi.NativeFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>>( - "ReplayIntegration__onConfigurationChanged") - .asFunction< - jni.JniResult Function( - ffi.Pointer, ffi.Pointer)>(); - - /// from: public void onConfigurationChanged(android.content.res.Configuration configuration) - void onConfigurationChanged( - jni.JObject configuration, - ) { - return _onConfigurationChanged(reference, configuration.reference).check(); - } - - static final _onLowMemory = jniLookup< - ffi - .NativeFunction)>>( - "ReplayIntegration__onLowMemory") - .asFunction)>(); - - /// from: public void onLowMemory() - void onLowMemory() { - return _onLowMemory(reference).check(); - } -} - -class $ReplayIntegrationType extends jni.JObjType { - const $ReplayIntegrationType(); - - @override - String get signature => r"Lio/sentry/android/replay/ReplayIntegration;"; - - @override - ReplayIntegration fromRef(jni.JObjectPtr ref) => - ReplayIntegration.fromRef(ref); - - @override - jni.JObjType get superType => const jni.JObjectType(); - - @override - final superCount = 1; - - @override - int get hashCode => ($ReplayIntegrationType).hashCode; - - @override - bool operator ==(Object other) { - return other.runtimeType == ($ReplayIntegrationType) && - other is $ReplayIntegrationType; - } -} diff --git a/flutter/lib/src/native/java/sentry_native_java.dart b/flutter/lib/src/native/java/sentry_native_java.dart index f83b1879cb..20774e11fa 100644 --- a/flutter/lib/src/native/java/sentry_native_java.dart +++ b/flutter/lib/src/native/java/sentry_native_java.dart @@ -1,24 +1,10 @@ -import 'package:jni/jni.dart'; import 'package:meta/meta.dart'; -import '../../sentry_flutter_options.dart'; import '../sentry_native_channel.dart'; -import 'android_replay_recorder.dart'; -import 'binding.dart' as java; +// Note: currently this doesn't do anything. Later, it shall be used with +// generated JNI bindings. See https://github.com/getsentry/sentry-dart/issues/1444 @internal class SentryNativeJava extends SentryNativeChannel { SentryNativeJava(super.channel); - - @override - Future init(SentryFlutterOptions options) async { - if (options.replay.isEnabled) { - // Necessary for the generated binding to work as of jnigen v0.6.0 - // This may change in the future. - Jni.initDLApi(); - - java.SentryFlutterReplay.recorder = AndroidReplayRecorder.create(options); - } - return super.init(options); - } } diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 16f8c37531..d346e6d0c3 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -27,7 +27,6 @@ dependencies: package_info_plus: '>=1.0.0' meta: ^1.3.0 ffi: ^2.0.0 - jni: 0.6.0 path: ^1.8.0 dev_dependencies: @@ -41,7 +40,6 @@ dev_dependencies: remove_from_coverage: ^2.0.0 flutter_localizations: sdk: flutter - jnigen: 0.6.0 flutter: plugin: From af22a59cf2e2b10f72aae23f97f5d58312758786 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 6 May 2024 15:46:49 +0200 Subject: [PATCH 36/74] wip: methodchannel based android recorder --- .../io/sentry/flutter/SentryFlutterPlugin.kt | 22 ++--- .../io/sentry/flutter/SentryFlutterReplay.kt | 13 --- .../flutter/SentryFlutterReplayRecorder.kt | 40 +++++++++ .../native/java/android_replay_recorder.dart | 86 ------------------- .../src/native/java/sentry_native_java.dart | 77 +++++++++++++++++ .../lib/src/native/sentry_native_channel.dart | 38 ++++---- flutter/lib/src/replay/recorder.dart | 1 - 7 files changed, 147 insertions(+), 130 deletions(-) delete mode 100644 flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt create mode 100644 flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt delete mode 100644 flutter/lib/src/native/java/android_replay_recorder.dart diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index bd9220b065..c511d896d1 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -132,21 +132,21 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { // Replace the default ReplayIntegration with a custom one that uses a Flutter-specific recorder. options.integrations.removeAll { it is ReplayIntegration } - val recorder = SentryFlutterReplay.recorder - if (recorder != null && options.cacheDirPath != null) { + val cacheDirPath = options.cacheDirPath + if (cacheDirPath != null && + (options.experimental.sessionReplay.isSessionReplayEnabled || + options.experimental.sessionReplay.isSessionReplayForErrorsEnabled)) { val replay = - ReplayIntegration( - context, - dateProvider = CurrentDateProvider.getInstance(), - recorderProvider = { recorder }, - recorderConfigProvider = null, - replayCacheProvider = null, - ) + ReplayIntegration( + context, + dateProvider = CurrentDateProvider.getInstance(), + recorderProvider = { SentryFlutterReplayRecorder(channel, cacheDirPath) }, + recorderConfigProvider = null, + replayCacheProvider = null, + ) options.addIntegration(replay) options.setReplayController(replay) - - SentryFlutterReplay.integration = replay } else { options.setReplayController(null) } diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt deleted file mode 100644 index 7266d852e4..0000000000 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplay.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.sentry.flutter - -import io.sentry.android.replay.Recorder -import io.sentry.android.replay.ReplayIntegration - -object SentryFlutterReplay { - // Set by the Flutter side, read during SentryAndroid.init(). If null, replay is disabled. - @JvmField - var recorder: Recorder? = null - - // Set by SentryAndroid.init(), read by the Flutter side in recorder.start() - lateinit var integration: ReplayIntegration -} diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt new file mode 100644 index 0000000000..16de3d5426 --- /dev/null +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt @@ -0,0 +1,40 @@ +package io.sentry.flutter + +import android.os.Handler; +import android.os.Looper; +import io.flutter.plugin.common.MethodChannel +import io.sentry.android.replay.Recorder +import io.sentry.android.replay.ScreenshotRecorderConfig + +internal class SentryFlutterReplayRecorder( + private val channel: MethodChannel, + private val cacheDirPath: String +) : Recorder { + override fun start(recorderConfig: ScreenshotRecorderConfig) { + Handler(Looper.getMainLooper()).post { + channel.invokeMethod( + "start", + mapOf( + "directory" to cacheDirPath, + "width" to recorderConfig.recordingWidth, + "height" to recorderConfig.recordingHeight, + "frameRate" to recorderConfig.frameRate)) + } + } + + override fun resume() { + Handler(Looper.getMainLooper()).post { channel.invokeMethod("resume", null) } + } + + override fun pause() { + Handler(Looper.getMainLooper()).post { channel.invokeMethod("pause", null) } + } + + override fun stop() { + Handler(Looper.getMainLooper()).post { channel.invokeMethod("stop", null) } + } + + override fun close() { + stop() + } +} diff --git a/flutter/lib/src/native/java/android_replay_recorder.dart b/flutter/lib/src/native/java/android_replay_recorder.dart deleted file mode 100644 index 9677efd9fa..0000000000 --- a/flutter/lib/src/native/java/android_replay_recorder.dart +++ /dev/null @@ -1,86 +0,0 @@ -import 'dart:io'; -import 'dart:ui'; - -import 'package:jni/jni.dart'; -import 'package:meta/meta.dart'; -import 'package:path/path.dart' as path; - -import '../../replay/recorder.dart'; -import '../../replay/recorder_config.dart'; -import '../../sentry_flutter_options.dart'; -import 'binding.dart' as java; - -@internal -class AndroidReplayRecorder implements java.$RecorderImpl { - final SentryFlutterOptions _options; - late ScreenshotRecorder _recorder; - late java.ReplayIntegration? _integration; - - AndroidReplayRecorder._(this._options); - - static java.Recorder create(SentryFlutterOptions options) => - java.Recorder.implement(AndroidReplayRecorder._(options)); - - @override - void pause() => _recorder.stop(); - - @override - void resume() => _recorder.start(); - - @override - void start(java.ScreenshotRecorderConfig config) { - _integration = java.SentryFlutterReplay.integration; - - var jniCacheDir = _integration!.getReplayCacheDir(); - var cacheDir = - jniCacheDir.getAbsolutePath().toDartString(releaseOriginal: true); - jniCacheDir.release(); - - // Note: time measurements using a Stopwatch in a debug build: - // save as rawRgba (1230876 bytes): 0.257 ms -- discarded - // save as PNG (25401 bytes): 43.110 ms -- used for the final image - // image size: 25401 bytes - // save to file: 3.677 ms - // new jfile: 0.400 ms - // onScreenshotRecorded1: 1.237 ms - // released and exiting callback: 0.021 ms - ScreenshotRecorderCallback callback = (image) async { - var imageData = await image.toByteData(format: ImageByteFormat.png); - if (imageData != null) { - var timestamp = DateTime.now().millisecondsSinceEpoch; - var filePath = path.join(cacheDir, "$timestamp.png"); - await File(filePath).writeAsBytes(imageData.buffer.asUint8List()); - - var jFilePath = filePath.toJString(); - var jFile = java.File(jFilePath); - try { - _integration?.onScreenshotRecorded1(jFile, timestamp); - } finally { - jFile.release(); - jFilePath.release(); - } - } - }; - - _recorder = ScreenshotRecorder( - ScreenshotRecorderConfig( - config.getRecordingWidth(), - config.getRecordingHeight(), - config.getFrameRate(), - ), - callback, - _options.logger, - _options.replay, - ); - - _recorder.start(); - } - - @override - void stop() { - _recorder.stop(); - var integration = _integration; - _integration = null; - integration?.release(); - } -} diff --git a/flutter/lib/src/native/java/sentry_native_java.dart b/flutter/lib/src/native/java/sentry_native_java.dart index 20774e11fa..d4845d4c94 100644 --- a/flutter/lib/src/native/java/sentry_native_java.dart +++ b/flutter/lib/src/native/java/sentry_native_java.dart @@ -1,10 +1,87 @@ +import 'dart:io'; +import 'dart:ui'; + import 'package:meta/meta.dart'; +import 'package:path/path.dart' as path; +import '../../../sentry_flutter.dart'; +import '../../replay/recorder.dart'; +import '../../replay/recorder_config.dart'; import '../sentry_native_channel.dart'; // Note: currently this doesn't do anything. Later, it shall be used with // generated JNI bindings. See https://github.com/getsentry/sentry-dart/issues/1444 @internal class SentryNativeJava extends SentryNativeChannel { + ScreenshotRecorder? _replayRecorder; + late final SentryFlutterOptions _options; SentryNativeJava(super.channel); + + @override + Future init(SentryFlutterOptions options) async { + _options = options; + channel.setMethodCallHandler((call) async { + switch (call.method) { + case 'ReplayRecorder.start': + _startRecorder( + call.arguments['directory'] as String, + ScreenshotRecorderConfig( + call.arguments['frameRate'] as int, + call.arguments['width'] as int, + call.arguments['height'] as int, + ), + ); + break; + case 'ReplayRecorder.stop': + _replayRecorder?.stop(); + _replayRecorder = null; + break; + case 'ReplayRecorder.pause': + _replayRecorder?.stop(); + break; + case 'ReplayRecorder.resume': + _replayRecorder?.start(); + break; + default: + throw UnimplementedError('Method ${call.method} not implemented'); + } + }); + return super.init(options); + } + + void _startRecorder(String cacheDir, ScreenshotRecorderConfig config) { + // Note: time measurements using a Stopwatch in a debug build: + // save as rawRgba (1230876 bytes): 0.257 ms -- discarded + // save as PNG (25401 bytes): 43.110 ms -- used for the final image + // image size: 25401 bytes + // save to file: 3.677 ms + // onScreenshotRecorded1: 1.237 ms + // released and exiting callback: 0.021 ms + ScreenshotRecorderCallback callback = (image) async { + var imageData = await image.toByteData(format: ImageByteFormat.png); + if (imageData != null) { + var timestamp = DateTime.now().millisecondsSinceEpoch; + var filePath = path.join(cacheDir, "$timestamp.png"); + await File(filePath).writeAsBytes(imageData.buffer.asUint8List()); + + try { + await channel.invokeMethod( + 'ReplayRecorder.onScreenshotRecorded', + {'path': filePath, 'timestamp': timestamp}, + ); + } catch (error, stackTrace) { + _options.logger( + SentryLevel.error, + 'Native call `ReplayRecorder.onScreenshotRecorded` failed', + exception: error, + stackTrace: stackTrace, + ); + } + } + }; + + _replayRecorder = + ScreenshotRecorder(config, callback, _options.logger, _options.replay) + ..start(); + } } diff --git a/flutter/lib/src/native/sentry_native_channel.dart b/flutter/lib/src/native/sentry_native_channel.dart index 6961550229..3b2742d1c5 100644 --- a/flutter/lib/src/native/sentry_native_channel.dart +++ b/flutter/lib/src/native/sentry_native_channel.dart @@ -11,15 +11,16 @@ import 'sentry_native_binding.dart'; /// Provide typed methods to access native layer via MethodChannel. @internal class SentryNativeChannel implements SentryNativeBinding { - SentryNativeChannel(this._channel); + SentryNativeChannel(this.channel); - final MethodChannel _channel; + @protected + final MethodChannel channel; // TODO Move other native calls here. @override Future init(SentryFlutterOptions options) async => - _channel.invokeMethod('initNativeSdk', { + channel.invokeMethod('initNativeSdk', { 'dsn': options.dsn, 'debug': options.debug, 'environment': options.environment, @@ -62,22 +63,21 @@ class SentryNativeChannel implements SentryNativeBinding { }); @override - Future close() async => _channel.invokeMethod('closeNativeSdk'); + Future close() async => channel.invokeMethod('closeNativeSdk'); @override Future fetchNativeAppStart() async { final json = - await _channel.invokeMapMethod('fetchNativeAppStart'); + await channel.invokeMapMethod('fetchNativeAppStart'); return (json != null) ? NativeAppStart.fromJson(json) : null; } @override - Future beginNativeFrames() => - _channel.invokeMethod('beginNativeFrames'); + Future beginNativeFrames() => channel.invokeMethod('beginNativeFrames'); @override Future endNativeFrames(SentryId id) async { - final json = await _channel.invokeMapMethod( + final json = await channel.invokeMapMethod( 'endNativeFrames', {'id': id.toString()}); return (json != null) ? NativeFrames.fromJson(json) : null; } @@ -87,7 +87,7 @@ class SentryNativeChannel implements SentryNativeBinding { final normalizedUser = user?.copyWith( data: MethodChannelHelper.normalizeMap(user.data), ); - await _channel.invokeMethod( + await channel.invokeMethod( 'setUser', {'user': normalizedUser?.toJson()}, ); @@ -98,42 +98,42 @@ class SentryNativeChannel implements SentryNativeBinding { final normalizedBreadcrumb = breadcrumb.copyWith( data: MethodChannelHelper.normalizeMap(breadcrumb.data), ); - await _channel.invokeMethod( + await channel.invokeMethod( 'addBreadcrumb', {'breadcrumb': normalizedBreadcrumb.toJson()}, ); } @override - Future clearBreadcrumbs() => _channel.invokeMethod('clearBreadcrumbs'); + Future clearBreadcrumbs() => channel.invokeMethod('clearBreadcrumbs'); @override - Future setContexts(String key, dynamic value) => _channel.invokeMethod( + Future setContexts(String key, dynamic value) => channel.invokeMethod( 'setContexts', {'key': key, 'value': MethodChannelHelper.normalize(value)}, ); @override Future removeContexts(String key) => - _channel.invokeMethod('removeContexts', {'key': key}); + channel.invokeMethod('removeContexts', {'key': key}); @override - Future setExtra(String key, dynamic value) => _channel.invokeMethod( + Future setExtra(String key, dynamic value) => channel.invokeMethod( 'setExtra', {'key': key, 'value': MethodChannelHelper.normalize(value)}, ); @override Future removeExtra(String key) => - _channel.invokeMethod('removeExtra', {'key': key}); + channel.invokeMethod('removeExtra', {'key': key}); @override Future setTag(String key, String value) => - _channel.invokeMethod('setTag', {'key': key, 'value': value}); + channel.invokeMethod('setTag', {'key': key, 'value': value}); @override Future removeTag(String key) => - _channel.invokeMethod('removeTag', {'key': key}); + channel.invokeMethod('removeTag', {'key': key}); @override int? startProfiler(SentryId traceId) => @@ -141,12 +141,12 @@ class SentryNativeChannel implements SentryNativeBinding { @override Future discardProfiler(SentryId traceId) => - _channel.invokeMethod('discardProfiler', traceId.toString()); + channel.invokeMethod('discardProfiler', traceId.toString()); @override Future?> collectProfile( SentryId traceId, int startTimeNs, int endTimeNs) => - _channel.invokeMapMethod('collectProfile', { + channel.invokeMapMethod('collectProfile', { 'traceId': traceId.toString(), 'startTime': startTimeNs, 'endTime': endTimeNs, diff --git a/flutter/lib/src/replay/recorder.dart b/flutter/lib/src/replay/recorder.dart index 801a145782..8f67b314a0 100644 --- a/flutter/lib/src/replay/recorder.dart +++ b/flutter/lib/src/replay/recorder.dart @@ -6,7 +6,6 @@ import 'package:flutter/rendering.dart'; import 'package:meta/meta.dart'; import '../../sentry_flutter.dart'; -import '../sentry_replay_options.dart'; import 'recorder_config.dart'; import 'recorder_widget_filter.dart'; import 'scheduler.dart'; From 6bf1f00374e828c6bc680efe53a70ea83e0b5445 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 6 May 2024 16:05:47 +0200 Subject: [PATCH 37/74] callback --- .../io/sentry/flutter/SentryFlutterPlugin.kt | 21 +++++++++++++++++-- .../src/native/java/sentry_native_java.dart | 4 ++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index c511d896d1..079cfc1147 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -30,11 +30,13 @@ import io.sentry.protocol.SentryId import io.sentry.protocol.User import io.sentry.transport.CurrentDateProvider import java.lang.ref.WeakReference +import java.io.File class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { private lateinit var channel: MethodChannel private lateinit var context: Context private lateinit var sentryFlutter: SentryFlutter + private lateinit var replay: ReplayIntegration private var activity: WeakReference? = null private var framesTracker: ActivityFramesTracker? = null @@ -77,6 +79,7 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { "setTag" -> setTag(call.argument("key"), call.argument("value"), result) "removeTag" -> removeTag(call.argument("key"), result) "loadContexts" -> loadContexts(result) + "addReplayScreenshot" -> addReplayScreenshot(call.argument("path"), call.argument("timestamp"), result) else -> result.notImplemented() } } @@ -130,13 +133,13 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { options.beforeSend = BeforeSendCallbackImpl(options.sdkVersion) - // Replace the default ReplayIntegration with a custom one that uses a Flutter-specific recorder. + // Replace the default ReplayIntegration with a Flutter-specific recorder. options.integrations.removeAll { it is ReplayIntegration } val cacheDirPath = options.cacheDirPath if (cacheDirPath != null && (options.experimental.sessionReplay.isSessionReplayEnabled || options.experimental.sessionReplay.isSessionReplayForErrorsEnabled)) { - val replay = + replay = ReplayIntegration( context, dateProvider = CurrentDateProvider.getInstance(), @@ -468,4 +471,18 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { ) result.success(serializedScope) } + + private fun addReplayScreenshot( + path: String?, + timestamp: Long?, + result: Result, + ) { + if (path == null || timestamp == null) { + result.error("5", "Arguments are null", null) + return + } + replay.onScreenshotRecorded(File(path), timestamp) + result.success("") + } + } diff --git a/flutter/lib/src/native/java/sentry_native_java.dart b/flutter/lib/src/native/java/sentry_native_java.dart index d4845d4c94..cdd3c46bbd 100644 --- a/flutter/lib/src/native/java/sentry_native_java.dart +++ b/flutter/lib/src/native/java/sentry_native_java.dart @@ -66,13 +66,13 @@ class SentryNativeJava extends SentryNativeChannel { try { await channel.invokeMethod( - 'ReplayRecorder.onScreenshotRecorded', + 'addReplayScreenshot', {'path': filePath, 'timestamp': timestamp}, ); } catch (error, stackTrace) { _options.logger( SentryLevel.error, - 'Native call `ReplayRecorder.onScreenshotRecorded` failed', + 'Native call `addReplayScreenshot` failed', exception: error, stackTrace: stackTrace, ); From 9dc14aac81de3f2269036315847cd7583a67f715 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 6 May 2024 21:01:57 +0200 Subject: [PATCH 38/74] linter issues --- .../io/sentry/flutter/SentryFlutterPlugin.kt | 22 +++++++++---------- .../flutter/SentryFlutterReplayRecorder.kt | 22 ++++++++++--------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index 079cfc1147..8c8c2e2446 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -29,8 +29,8 @@ import io.sentry.protocol.SdkVersion import io.sentry.protocol.SentryId import io.sentry.protocol.User import io.sentry.transport.CurrentDateProvider -import java.lang.ref.WeakReference import java.io.File +import java.lang.ref.WeakReference class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { private lateinit var channel: MethodChannel @@ -136,17 +136,16 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { // Replace the default ReplayIntegration with a Flutter-specific recorder. options.integrations.removeAll { it is ReplayIntegration } val cacheDirPath = options.cacheDirPath - if (cacheDirPath != null && - (options.experimental.sessionReplay.isSessionReplayEnabled || - options.experimental.sessionReplay.isSessionReplayForErrorsEnabled)) { + val isReplayEnabled = options.experimental.sessionReplay.isSessionReplayEnabled || options.experimental.sessionReplay.isSessionReplayForErrorsEnabled + if (cacheDirPath != null && isReplayEnabled) { replay = - ReplayIntegration( - context, - dateProvider = CurrentDateProvider.getInstance(), - recorderProvider = { SentryFlutterReplayRecorder(channel, cacheDirPath) }, - recorderConfigProvider = null, - replayCacheProvider = null, - ) + ReplayIntegration( + context, + dateProvider = CurrentDateProvider.getInstance(), + recorderProvider = { SentryFlutterReplayRecorder(channel, cacheDirPath) }, + recorderConfigProvider = null, + replayCacheProvider = null, + ) options.addIntegration(replay) options.setReplayController(replay) @@ -484,5 +483,4 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { replay.onScreenshotRecorded(File(path), timestamp) result.success("") } - } diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt index 16de3d5426..0d922bd8e1 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt @@ -1,24 +1,26 @@ package io.sentry.flutter -import android.os.Handler; -import android.os.Looper; +import android.os.Handler +import android.os.Looper import io.flutter.plugin.common.MethodChannel import io.sentry.android.replay.Recorder import io.sentry.android.replay.ScreenshotRecorderConfig internal class SentryFlutterReplayRecorder( - private val channel: MethodChannel, - private val cacheDirPath: String + private val channel: MethodChannel, + private val cacheDirPath: String, ) : Recorder { override fun start(recorderConfig: ScreenshotRecorderConfig) { Handler(Looper.getMainLooper()).post { channel.invokeMethod( - "start", - mapOf( - "directory" to cacheDirPath, - "width" to recorderConfig.recordingWidth, - "height" to recorderConfig.recordingHeight, - "frameRate" to recorderConfig.frameRate)) + "start", + mapOf( + "directory" to cacheDirPath, + "width" to recorderConfig.recordingWidth, + "height" to recorderConfig.recordingHeight, + "frameRate" to recorderConfig.frameRate, + ), + ) } } From 3639f001d388ae91bf7762b4e72182de4c594fe1 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 6 May 2024 21:20:53 +0200 Subject: [PATCH 39/74] minor fixes --- .../flutter/SentryFlutterReplayRecorder.kt | 16 ++++++++-------- .../lib/src/native/java/sentry_native_java.dart | 6 +++--- flutter/lib/src/replay/recorder_config.dart | 3 ++- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt index 0d922bd8e1..f3c058a222 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt @@ -10,30 +10,30 @@ internal class SentryFlutterReplayRecorder( private val channel: MethodChannel, private val cacheDirPath: String, ) : Recorder { - override fun start(recorderConfig: ScreenshotRecorderConfig) { + override fun start(config: ScreenshotRecorderConfig) { Handler(Looper.getMainLooper()).post { channel.invokeMethod( - "start", + "ReplayRecorder.start", mapOf( "directory" to cacheDirPath, - "width" to recorderConfig.recordingWidth, - "height" to recorderConfig.recordingHeight, - "frameRate" to recorderConfig.frameRate, + "width" to config.recordingWidth, + "height" to config.recordingHeight, + "frameRate" to config.frameRate, ), ) } } override fun resume() { - Handler(Looper.getMainLooper()).post { channel.invokeMethod("resume", null) } + Handler(Looper.getMainLooper()).post { channel.invokeMethod("ReplayRecorder.resume", null) } } override fun pause() { - Handler(Looper.getMainLooper()).post { channel.invokeMethod("pause", null) } + Handler(Looper.getMainLooper()).post { channel.invokeMethod("ReplayRecorder.pause", null) } } override fun stop() { - Handler(Looper.getMainLooper()).post { channel.invokeMethod("stop", null) } + Handler(Looper.getMainLooper()).post { channel.invokeMethod("ReplayRecorder.stop", null) } } override fun close() { diff --git a/flutter/lib/src/native/java/sentry_native_java.dart b/flutter/lib/src/native/java/sentry_native_java.dart index cdd3c46bbd..5461b224a6 100644 --- a/flutter/lib/src/native/java/sentry_native_java.dart +++ b/flutter/lib/src/native/java/sentry_native_java.dart @@ -26,9 +26,9 @@ class SentryNativeJava extends SentryNativeChannel { _startRecorder( call.arguments['directory'] as String, ScreenshotRecorderConfig( - call.arguments['frameRate'] as int, - call.arguments['width'] as int, - call.arguments['height'] as int, + width: call.arguments['width'] as int, + height: call.arguments['height'] as int, + frameRate: call.arguments['frameRate'] as int, ), ); break; diff --git a/flutter/lib/src/replay/recorder_config.dart b/flutter/lib/src/replay/recorder_config.dart index cb66b30fed..b7e4fd4c86 100644 --- a/flutter/lib/src/replay/recorder_config.dart +++ b/flutter/lib/src/replay/recorder_config.dart @@ -6,5 +6,6 @@ class ScreenshotRecorderConfig { final int height; final int frameRate; - ScreenshotRecorderConfig(this.width, this.height, this.frameRate); + ScreenshotRecorderConfig( + {required this.width, required this.height, required this.frameRate}); } From 14ba7420860b6b5d116d4232ad1ad582726853be Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 6 May 2024 21:45:01 +0200 Subject: [PATCH 40/74] more fixes --- .../main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt | 2 +- .../io/sentry/flutter/SentryFlutterReplayRecorder.kt | 9 ++++++++- flutter/lib/src/native/java/sentry_native_java.dart | 6 ++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index 8c8c2e2446..a232f5d96a 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -142,7 +142,7 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { ReplayIntegration( context, dateProvider = CurrentDateProvider.getInstance(), - recorderProvider = { SentryFlutterReplayRecorder(channel, cacheDirPath) }, + recorderProvider = { SentryFlutterReplayRecorder(channel, replay) }, recorderConfigProvider = null, replayCacheProvider = null, ) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt index f3c058a222..34cf1404c5 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt @@ -4,13 +4,20 @@ import android.os.Handler import android.os.Looper import io.flutter.plugin.common.MethodChannel import io.sentry.android.replay.Recorder +import io.sentry.android.replay.ReplayIntegration import io.sentry.android.replay.ScreenshotRecorderConfig +// TODO try, catch, and log errors, otherwise the app will crash internal class SentryFlutterReplayRecorder( private val channel: MethodChannel, - private val cacheDirPath: String, + private val integration: ReplayIntegration, ) : Recorder { override fun start(config: ScreenshotRecorderConfig) { + val cacheDirPath = integration.replayCacheDir?.absolutePath + if (cacheDirPath == null) { + // TODO debug log + return + } Handler(Looper.getMainLooper()).post { channel.invokeMethod( "ReplayRecorder.start", diff --git a/flutter/lib/src/native/java/sentry_native_java.dart b/flutter/lib/src/native/java/sentry_native_java.dart index 5461b224a6..df9e50e354 100644 --- a/flutter/lib/src/native/java/sentry_native_java.dart +++ b/flutter/lib/src/native/java/sentry_native_java.dart @@ -62,6 +62,12 @@ class SentryNativeJava extends SentryNativeChannel { if (imageData != null) { var timestamp = DateTime.now().millisecondsSinceEpoch; var filePath = path.join(cacheDir, "$timestamp.png"); + + _options.logger( + SentryLevel.debug, + 'Replay: Saving screenshot to $filePath (' + '${image.width}x${image.height} pixels, ' + '${imageData.lengthInBytes} bytes)'); await File(filePath).writeAsBytes(imageData.buffer.asUint8List()); try { From 585386e23d92307ecda460fbe7123aa32d817e20 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 6 May 2024 21:47:38 +0200 Subject: [PATCH 41/74] linter issues --- .../src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index a232f5d96a..16109543e2 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -136,7 +136,8 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { // Replace the default ReplayIntegration with a Flutter-specific recorder. options.integrations.removeAll { it is ReplayIntegration } val cacheDirPath = options.cacheDirPath - val isReplayEnabled = options.experimental.sessionReplay.isSessionReplayEnabled || options.experimental.sessionReplay.isSessionReplayForErrorsEnabled + val replayOptions = options.experimental.sessionReplay + val isReplayEnabled = replayOptions.isSessionReplayEnabled || replayOptions.isSessionReplayForErrorsEnabled if (cacheDirPath != null && isReplayEnabled) { replay = ReplayIntegration( From ee1dbd69979f5ac9b44b07369126273e32e373f0 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 6 May 2024 22:23:49 +0200 Subject: [PATCH 42/74] cleanup --- flutter/android/proguard-rules.pro | 6 ---- .../app/src/main/cpp/native-sample.cpp | 32 ++++++++--------- flutter/lib/src/replay/recorder.dart | 34 ++++--------------- .../src/replay/recorder_widget_filter.dart | 29 ++++++++++++---- 4 files changed, 45 insertions(+), 56 deletions(-) diff --git a/flutter/android/proguard-rules.pro b/flutter/android/proguard-rules.pro index a82cd7fac6..d64759b4c6 100644 --- a/flutter/android/proguard-rules.pro +++ b/flutter/android/proguard-rules.pro @@ -1,11 +1,5 @@ -keep class io.sentry.flutter.** { *; } -# JNI generated binding code (keep up to date with ffi-jni.yaml). --keep class java.io.File --keep class io.sentry.android.replay.Recorder --keep class io.sentry.android.replay.ScreenshotRecorderConfig --keep class io.sentry.android.replay.ReplayIntegration - # To ensure that stack traces is unambiguous # https://developer.android.com/studio/build/shrink-code#decode-stack-trace -keepattributes LineNumberTable,SourceFile diff --git a/flutter/example/android/app/src/main/cpp/native-sample.cpp b/flutter/example/android/app/src/main/cpp/native-sample.cpp index 8b561d15c4..c84e77da41 100644 --- a/flutter/example/android/app/src/main/cpp/native-sample.cpp +++ b/flutter/example/android/app/src/main/cpp/native-sample.cpp @@ -1,26 +1,26 @@ -#include #include -// #include +#include +#include #define TAG "sentry-sample" extern "C" { -JNIEXPORT void JNICALL Java_io_sentry_samples_flutter_MainActivity_crash(JNIEnv *env, jclass cls) { - __android_log_print(ANDROID_LOG_WARN, TAG, "About to crash with a SEGFAULT in C++!"); - char *ptr = 0; - *ptr += 1; +JNIEXPORT void JNICALL +Java_io_sentry_samples_flutter_MainActivity_crash(JNIEnv *env, jclass cls) { + __android_log_print(ANDROID_LOG_WARN, TAG, + "About to crash with a SEGFAULT in C++!"); + char *ptr = 0; + *ptr += 1; } -JNIEXPORT void JNICALL Java_io_sentry_samples_flutter_MainActivity_message(JNIEnv *env, jclass cls) { - // FIXME temporarily removed to use local sentry-java build - // // __android_log_print(ANDROID_LOG_WARN, TAG, "Sending message."); - // sentry_value_t event = sentry_value_new_message_event( - // /* level */ SENTRY_LEVEL_INFO, - // /* logger */ "native", - // /* message */ "message from C++!" - // ); - // sentry_capture_event(event); +JNIEXPORT void JNICALL +Java_io_sentry_samples_flutter_MainActivity_message(JNIEnv *env, jclass cls) { + // __android_log_print(ANDROID_LOG_WARN, TAG, "Sending message."); + sentry_value_t event = sentry_value_new_message_event( + /* level */ SENTRY_LEVEL_INFO, + /* logger */ "native", + /* message */ "message from C++!"); + sentry_capture_event(event); } - } diff --git a/flutter/lib/src/replay/recorder.dart b/flutter/lib/src/replay/recorder.dart index 8f67b314a0..8b536ffb15 100644 --- a/flutter/lib/src/replay/recorder.dart +++ b/flutter/lib/src/replay/recorder.dart @@ -13,9 +13,6 @@ import 'scheduler.dart'; @internal typedef ScreenshotRecorderCallback = Future Function(Image); -// TODO evaluate [notifications](https://api.flutter.dev/flutter/widgets/Notification-class.html) -// to only collect screenshots when there are changes. -// We probably can't use build() because inner repaintboundaries won't propagate up? @internal class ScreenshotRecorder { final ScreenshotRecorderConfig _config; @@ -33,7 +30,8 @@ class ScreenshotRecorder { if (_options.redactAllText || _options.redactAllImages) { _widgetFilter = WidgetFilter( redactText: _options.redactAllText, - redactImages: _options.redactAllImages); + redactImages: _options.redactAllImages, + logger: _logger); } } @@ -63,9 +61,7 @@ class ScreenshotRecorder { } try { - // TODO remove these final watch = Stopwatch()..start(); - final watch2 = Stopwatch()..start(); // The desired resolution (coming from the configuration) is usually // rounded to next multitude of 16. Therefore, we scale the image. @@ -77,7 +73,6 @@ class ScreenshotRecorder { // First, we synchronously capture the image and enumarete widgets on the main UI loop. final futureImage = renderObject.toImage(pixelRatio: pixelRatio); - watch.printAndReset("renderObject.toImage($pixelRatio)"); final filter = _widgetFilter; if (filter != null) { @@ -86,39 +81,31 @@ class ScreenshotRecorder { Rect.fromLTWH(0, 0, srcWidth * pixelRatio, srcHeight * pixelRatio), ); context.visitChildElements(filter.obscure); - watch.printAndReset("collect widget boundaries"); } - final blockingTime = watch2.elapsedMilliseconds; + final blockingTime = watch.elapsedMilliseconds; // Then we draw the image and obscure collected coordinates asynchronously. final recorder = PictureRecorder(); final canvas = Canvas(recorder); final image = await futureImage; - watch.printAndReset("await image (${image.width}x${image.height})"); try { canvas.drawImage(image, Offset.zero, Paint()); - watch.printAndReset("drawImage()"); } finally { image.dispose(); } if (filter != null) { _obscureWidgets(canvas, filter.items); - watch.printAndReset("obscureWidgets(${filter.items.length} items)"); } final picture = recorder.endRecording(); - watch.printAndReset("endRecording()"); try { final finalImage = await picture.toImage( (srcWidth * pixelRatio).round(), (srcHeight * pixelRatio).round()); - watch.printAndReset( - "picture.toImage(${finalImage.width}x${finalImage.height})"); try { await _callback(finalImage); - watch.printAndReset("callback()"); } finally { finalImage.dispose(); } @@ -126,9 +113,10 @@ class ScreenshotRecorder { picture.dispose(); } - _logger(SentryLevel.debug, - "Replay: captured a screenshot in ${watch2.elapsedMilliseconds} ms ($blockingTime ms blocking)."); - watch2.printAndReset("complete capture"); + _logger( + SentryLevel.debug, + "Replay: captured a screenshot in ${watch.elapsedMilliseconds}" + " ms ($blockingTime ms blocking)."); } catch (e, stackTrace) { _logger(SentryLevel.error, "Replay: failed to capture screenshot.", exception: e, stackTrace: stackTrace); @@ -143,11 +131,3 @@ class ScreenshotRecorder { } } } - -extension _WatchPrinter on Stopwatch { - void printAndReset(String message) { - print( - "RECORDER | $message: ${(elapsedMicroseconds / 1000).toStringAsFixed(3)} ms"); - reset(); - } -} diff --git a/flutter/lib/src/replay/recorder_widget_filter.dart b/flutter/lib/src/replay/recorder_widget_filter.dart index 75663da578..2e6ce1318d 100644 --- a/flutter/lib/src/replay/recorder_widget_filter.dart +++ b/flutter/lib/src/replay/recorder_widget_filter.dart @@ -1,5 +1,8 @@ import 'package:flutter/widgets.dart'; import 'package:meta/meta.dart'; +import 'package:sentry/sentry.dart'; + +import '../../sentry_flutter.dart'; @internal class WidgetFilter { @@ -9,11 +12,14 @@ class WidgetFilter { late double _pixelRatio; late Rect _bounds; final List items = []; + final SentryLogger logger; - WidgetFilter({required this.redactText, required this.redactImages}); + WidgetFilter( + {required this.redactText, + required this.redactImages, + required this.logger}); void setupAndClear(double pixelRatio, Rect bounds) { - print("WidgetFilter setupAndClear()"); _pixelRatio = pixelRatio; _bounds = bounds; items.clear(); @@ -23,7 +29,7 @@ class WidgetFilter { final widget = element.widget; if (!_isVisible(widget)) { - print("WidgetFilter skipping invisible: $widget"); + _devlog("WidgetFilter skipping invisible: $widget"); return; } @@ -50,14 +56,14 @@ class WidgetFilter { final renderObject = element.renderObject; if (renderObject is! RenderBox) { - print( + _devlog( "WidgetFilter cannot obscure widget $widget, it's renderObject is not a RenderBox"); return false; } final size = element.size; if (size == null) { - print( + _devlog( "WidgetFilter cannot obscure widget $widget, it's renderObject has a null size"); return false; } @@ -72,12 +78,12 @@ class WidgetFilter { ); if (!rect.overlaps(_bounds)) { - print("WidgetFilter skipping offscreen: $widget"); + _devlog("WidgetFilter skipping offscreen: $widget"); return false; } items.add(WidgetFilterItem(color ?? _defaultColor, rect)); - print("WidgetFilter obscuring: $widget"); + _devlog("WidgetFilter obscuring: $widget"); return true; } @@ -95,6 +101,15 @@ class WidgetFilter { } return true; } + + // Should be completely trimmed out in production builds. + @pragma('vm:prefer-inline') + void _devlog(String message) { + assert(() { + logger(SentryLevel.debug, message); + return true; + }()); + } } class WidgetFilterItem { From 266a85a654fc753f356990ca747b924f3af20a22 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 6 May 2024 22:29:06 +0200 Subject: [PATCH 43/74] improve logging --- .../lib/src/replay/recorder_widget_filter.dart | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/flutter/lib/src/replay/recorder_widget_filter.dart b/flutter/lib/src/replay/recorder_widget_filter.dart index 2e6ce1318d..9075f039e9 100644 --- a/flutter/lib/src/replay/recorder_widget_filter.dart +++ b/flutter/lib/src/replay/recorder_widget_filter.dart @@ -13,6 +13,7 @@ class WidgetFilter { late Rect _bounds; final List items = []; final SentryLogger logger; + final Set _warnedWidgets = {}; WidgetFilter( {required this.redactText, @@ -56,15 +57,13 @@ class WidgetFilter { final renderObject = element.renderObject; if (renderObject is! RenderBox) { - _devlog( - "WidgetFilter cannot obscure widget $widget, it's renderObject is not a RenderBox"); + _cantObscure(widget, "it's renderObject is not a RenderBox"); return false; } final size = element.size; if (size == null) { - _devlog( - "WidgetFilter cannot obscure widget $widget, it's renderObject has a null size"); + _cantObscure(widget, "it's renderObject has a null size"); return false; } @@ -110,6 +109,15 @@ class WidgetFilter { return true; }()); } + + @pragma('vm:prefer-inline') + void _cantObscure(Widget widget, String message) { + if (!_warnedWidgets.contains(widget)) { + _warnedWidgets.add(widget); + logger(SentryLevel.warning, + "WidgetFilter cannot obscure widget $widget: $message"); + } + } } class WidgetFilterItem { From 509c15fdd7247547a6dc15faaeedd1412b4b2a05 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 7 May 2024 09:25:28 +0200 Subject: [PATCH 44/74] move replay to experimental, same as in other SDKs --- flutter/example/lib/main.dart | 2 +- .../src/native/java/sentry_native_java.dart | 9 ++++--- .../lib/src/native/sentry_native_channel.dart | 4 +-- flutter/lib/src/sentry_flutter_options.dart | 25 ++++++++++++------- .../integrations/init_native_sdk_test.dart | 4 +-- 5 files changed, 27 insertions(+), 17 deletions(-) diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index f049cc677f..0fa822ff54 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -90,7 +90,7 @@ Future setupSentry( options.maxResponseBodySize = MaxResponseBodySize.always; options.navigatorKey = navigatorKey; - options.replay.sessionSampleRate = 1.0; + options.experimental.replay.sessionSampleRate = 1.0; _isIntegrationTest = isIntegrationTest; if (_isIntegrationTest) { diff --git a/flutter/lib/src/native/java/sentry_native_java.dart b/flutter/lib/src/native/java/sentry_native_java.dart index df9e50e354..35031eecd5 100644 --- a/flutter/lib/src/native/java/sentry_native_java.dart +++ b/flutter/lib/src/native/java/sentry_native_java.dart @@ -86,8 +86,11 @@ class SentryNativeJava extends SentryNativeChannel { } }; - _replayRecorder = - ScreenshotRecorder(config, callback, _options.logger, _options.replay) - ..start(); + _replayRecorder = ScreenshotRecorder( + config, + callback, + _options.logger, + _options.experimental.replay, + )..start(); } } diff --git a/flutter/lib/src/native/sentry_native_channel.dart b/flutter/lib/src/native/sentry_native_channel.dart index 3b2742d1c5..b3ddabbf2a 100644 --- a/flutter/lib/src/native/sentry_native_channel.dart +++ b/flutter/lib/src/native/sentry_native_channel.dart @@ -57,8 +57,8 @@ class SentryNativeChannel implements SentryNativeBinding { 'appHangTimeoutIntervalMillis': options.appHangTimeoutInterval.inMilliseconds, 'replay': { - 'sessionSampleRate': options.replay.sessionSampleRate, - 'errorSampleRate': options.replay.errorSampleRate, + 'sessionSampleRate': options.experimental.replay.sessionSampleRate, + 'errorSampleRate': options.experimental.replay.errorSampleRate, }, }); diff --git a/flutter/lib/src/sentry_flutter_options.dart b/flutter/lib/src/sentry_flutter_options.dart index 24f58a975b..e43c1433d2 100644 --- a/flutter/lib/src/sentry_flutter_options.dart +++ b/flutter/lib/src/sentry_flutter_options.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:meta/meta.dart'; +import 'package:meta/meta.dart' as meta; import 'package:sentry/sentry.dart'; import 'package:flutter/widgets.dart'; @@ -204,14 +204,14 @@ class SentryFlutterOptions extends SentryOptions { /// Sets the Proguard uuid for Android platform. String? proguardUuid; - @internal + @meta.internal late RendererWrapper rendererWrapper = RendererWrapper(); /// Enables the View Hierarchy feature. /// /// Renders an ASCII represention of the entire view hierarchy of the /// application when an error happens and includes it as an attachment. - @experimental + @meta.experimental bool attachViewHierarchy = false; /// When enabled, the SDK tracks when the application stops responding for a @@ -281,14 +281,14 @@ class SentryFlutterOptions extends SentryOptions { } /// Setting this to a custom [BindingWrapper] allows you to use a custom [WidgetsBinding]. - @experimental + @meta.experimental BindingWrapper bindingUtils = BindingWrapper(); /// The sample rate for profiling traces in the range of 0.0 to 1.0. /// This is relative to tracesSampleRate - it is a ratio of profiled traces out of all sampled traces. /// At the moment, only apps targeting iOS and macOS are supported. @override - @experimental + @meta.experimental double? get profilesSampleRate { // ignore: invalid_use_of_internal_member return super.profilesSampleRate; @@ -298,7 +298,7 @@ class SentryFlutterOptions extends SentryOptions { /// This is relative to tracesSampleRate - it is a ratio of profiled traces out of all sampled traces. /// At the moment, only apps targeting iOS and macOS are supported. @override - @experimental + @meta.experimental set profilesSampleRate(double? value) { // ignore: invalid_use_of_internal_member super.profilesSampleRate = value; @@ -307,9 +307,16 @@ class SentryFlutterOptions extends SentryOptions { /// The [navigatorKey] is used to add information of the currently used locale to the contexts. GlobalKey? navigatorKey; - /// Configuration of the experimental replay feature. - @experimental - final SentryReplayOptions replay = SentryReplayOptions(); + /// Configuration of experimental features that may change or be removed + /// without prior notice. Additionally, these features may not be ready for + /// production use yet. + @meta.experimental + final experimental = _SentryFlutterExperimentalOptions(); +} + +class _SentryFlutterExperimentalOptions { + /// Replay recording configuration. + final replay = SentryReplayOptions(); } /// Callback being executed in [ScreenshotEventProcessor], deciding if a diff --git a/flutter/test/integrations/init_native_sdk_test.dart b/flutter/test/integrations/init_native_sdk_test.dart index 982e9ae482..060d41b5f9 100644 --- a/flutter/test/integrations/init_native_sdk_test.dart +++ b/flutter/test/integrations/init_native_sdk_test.dart @@ -108,8 +108,8 @@ void main() { ..connectionTimeout = Duration(milliseconds: 9001) ..readTimeout = Duration(milliseconds: 9002) ..appHangTimeoutInterval = Duration(milliseconds: 9003) - ..replay.sessionSampleRate = 0.1 - ..replay.errorSampleRate = 0.2; + ..experimental.replay.sessionSampleRate = 0.1 + ..experimental.replay.errorSampleRate = 0.2; options.sdk.addIntegration('foo'); options.sdk.addPackage('bar', '1'); From 960f2da919f72ee5564b204a611d33ee145ec39b Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 7 May 2024 09:37:22 +0200 Subject: [PATCH 45/74] improve tree shaking --- .../src/native/java/sentry_native_java.dart | 59 ++++++++++--------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/flutter/lib/src/native/java/sentry_native_java.dart b/flutter/lib/src/native/java/sentry_native_java.dart index 35031eecd5..037db9b2a5 100644 --- a/flutter/lib/src/native/java/sentry_native_java.dart +++ b/flutter/lib/src/native/java/sentry_native_java.dart @@ -19,33 +19,38 @@ class SentryNativeJava extends SentryNativeChannel { @override Future init(SentryFlutterOptions options) async { - _options = options; - channel.setMethodCallHandler((call) async { - switch (call.method) { - case 'ReplayRecorder.start': - _startRecorder( - call.arguments['directory'] as String, - ScreenshotRecorderConfig( - width: call.arguments['width'] as int, - height: call.arguments['height'] as int, - frameRate: call.arguments['frameRate'] as int, - ), - ); - break; - case 'ReplayRecorder.stop': - _replayRecorder?.stop(); - _replayRecorder = null; - break; - case 'ReplayRecorder.pause': - _replayRecorder?.stop(); - break; - case 'ReplayRecorder.resume': - _replayRecorder?.start(); - break; - default: - throw UnimplementedError('Method ${call.method} not implemented'); - } - }); + // We only need these when replay is enabled so let's set it up + // conditionally. This allows Dart to trim the code. + if (options.experimental.replay.isEnabled) { + _options = options; + channel.setMethodCallHandler((call) async { + switch (call.method) { + case 'ReplayRecorder.start': + _startRecorder( + call.arguments['directory'] as String, + ScreenshotRecorderConfig( + width: call.arguments['width'] as int, + height: call.arguments['height'] as int, + frameRate: call.arguments['frameRate'] as int, + ), + ); + break; + case 'ReplayRecorder.stop': + _replayRecorder?.stop(); + _replayRecorder = null; + break; + case 'ReplayRecorder.pause': + _replayRecorder?.stop(); + break; + case 'ReplayRecorder.resume': + _replayRecorder?.start(); + break; + default: + throw UnimplementedError('Method ${call.method} not implemented'); + } + }); + } + return super.init(options); } From 95e3a3443df3f8739b295450891e18d0a7842c46 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 7 May 2024 11:37:30 +0200 Subject: [PATCH 46/74] test: scheduler --- flutter/lib/src/replay/scheduler.dart | 15 ++++- flutter/test/replay/scheduler_test.dart | 84 +++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 flutter/test/replay/scheduler_test.dart diff --git a/flutter/lib/src/replay/scheduler.dart b/flutter/lib/src/replay/scheduler.dart index 6d5ba2157c..421d57a323 100644 --- a/flutter/lib/src/replay/scheduler.dart +++ b/flutter/lib/src/replay/scheduler.dart @@ -1,4 +1,5 @@ import 'package:flutter/rendering.dart'; +import 'package:flutter/scheduler.dart'; import 'package:meta/meta.dart'; @internal @@ -17,7 +18,15 @@ class Scheduler { bool _running = false; bool _scheduled = false; - Scheduler(this._interval, this._callback); + final void Function(FrameCallback callback, {String debugLabel}) + _addPostFrameCallback; + + Scheduler(this._interval, this._callback) + : _addPostFrameCallback = RendererBinding.instance.addPostFrameCallback; + + @visibleForTesting + Scheduler.withCustomFrameTiming( + this._interval, this._callback, this._addPostFrameCallback); void start() { _running = true; @@ -30,6 +39,7 @@ class Scheduler { _running = false; } + @pragma('vm:prefer-inline') void _scheduleNext() { if (!_scheduled) { _scheduled = true; @@ -37,8 +47,9 @@ class Scheduler { } } + @pragma('vm:prefer-inline') void _runAfterNextFrame() { - RendererBinding.instance.addPostFrameCallback(_run); + _addPostFrameCallback(_run); } void _run(Duration sinceSchedulerEpoch) { diff --git a/flutter/test/replay/scheduler_test.dart b/flutter/test/replay/scheduler_test.dart new file mode 100644 index 0000000000..59ee5ef17b --- /dev/null +++ b/flutter/test/replay/scheduler_test.dart @@ -0,0 +1,84 @@ +import 'dart:io'; + +import 'package:flutter/scheduler.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:sentry_flutter/src/replay/scheduler.dart'; + +void main() { + group('$Scheduler', () { + test('does not trigger callback between frames', () { + var fixture = _Fixture.started(); + + expect(fixture.calls, 0); + sleep(const Duration(milliseconds: 100)); + expect(fixture.calls, 0); + }); + + test('triggers callback after a frame', () { + var fixture = _Fixture(); + fixture.sut.start(); + + expect(fixture.calls, 0); + fixture.drawFrame(); + expect(fixture.calls, 1); + fixture.drawFrame(); + fixture.drawFrame(); + fixture.drawFrame(); + expect(fixture.calls, 4); + }); + + test('does not trigger when stopped', () { + var fixture = _Fixture(); + fixture.sut.start(); + + expect(fixture.calls, 0); + fixture.drawFrame(); + expect(fixture.calls, 1); + fixture.drawFrame(); + fixture.sut.stop(); + fixture.drawFrame(); + expect(fixture.calls, 2); + }); + + test('triggers after a restart', () { + var fixture = _Fixture(); + fixture.sut.start(); + + expect(fixture.calls, 0); + fixture.drawFrame(); + expect(fixture.calls, 1); + fixture.sut.stop(); + fixture.drawFrame(); + expect(fixture.calls, 1); + fixture.sut.start(); + fixture.drawFrame(); + expect(fixture.calls, 2); + }); + }); +} + +class _Fixture { + var calls = 0; + late final Scheduler sut; + late FrameCallback registeredCallback; + var _frames = 0; + + _Fixture() { + sut = Scheduler.withCustomFrameTiming( + const Duration(milliseconds: 1), + (_) async => calls++, + (FrameCallback callback, {String debugLabel = 'callback'}) { + registeredCallback = callback; + }, + ); + } + + factory _Fixture.started() { + return _Fixture()..sut.start(); + } + + void drawFrame() { + _frames++; + registeredCallback(Duration(milliseconds: _frames)); + } +} From aa28200b25e0de847a0a9e740ac58fad8e086ba7 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 7 May 2024 12:41:42 +0200 Subject: [PATCH 47/74] support browser test --- flutter/test/replay/scheduler_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter/test/replay/scheduler_test.dart b/flutter/test/replay/scheduler_test.dart index 59ee5ef17b..0108766fb1 100644 --- a/flutter/test/replay/scheduler_test.dart +++ b/flutter/test/replay/scheduler_test.dart @@ -6,11 +6,11 @@ import 'package:sentry_flutter/src/replay/scheduler.dart'; void main() { group('$Scheduler', () { - test('does not trigger callback between frames', () { + test('does not trigger callback between frames', () async { var fixture = _Fixture.started(); expect(fixture.calls, 0); - sleep(const Duration(milliseconds: 100)); + await Future.delayed(const Duration(milliseconds: 100), () async {}); expect(fixture.calls, 0); }); From 16f36774f4b332bbd69c185cb78593933d53bded Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 7 May 2024 13:15:45 +0200 Subject: [PATCH 48/74] fix compat with old flutter --- flutter/lib/src/replay/scheduler.dart | 3 +-- flutter/test/replay/scheduler_test.dart | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/flutter/lib/src/replay/scheduler.dart b/flutter/lib/src/replay/scheduler.dart index 421d57a323..1696882c3b 100644 --- a/flutter/lib/src/replay/scheduler.dart +++ b/flutter/lib/src/replay/scheduler.dart @@ -18,8 +18,7 @@ class Scheduler { bool _running = false; bool _scheduled = false; - final void Function(FrameCallback callback, {String debugLabel}) - _addPostFrameCallback; + final void Function(FrameCallback callback) _addPostFrameCallback; Scheduler(this._interval, this._callback) : _addPostFrameCallback = RendererBinding.instance.addPostFrameCallback; diff --git a/flutter/test/replay/scheduler_test.dart b/flutter/test/replay/scheduler_test.dart index 0108766fb1..bf022a50bb 100644 --- a/flutter/test/replay/scheduler_test.dart +++ b/flutter/test/replay/scheduler_test.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:flutter/scheduler.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:sentry_flutter/src/replay/scheduler.dart'; From 86db5c4aa2cc338ba5d06b57da1ccda9ccbfceb1 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 7 May 2024 13:19:33 +0200 Subject: [PATCH 49/74] cleanup --- flutter/test/replay/scheduler_test.dart | 82 ++++++++++++------------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/flutter/test/replay/scheduler_test.dart b/flutter/test/replay/scheduler_test.dart index bf022a50bb..8352790707 100644 --- a/flutter/test/replay/scheduler_test.dart +++ b/flutter/test/replay/scheduler_test.dart @@ -3,55 +3,53 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:sentry_flutter/src/replay/scheduler.dart'; void main() { - group('$Scheduler', () { - test('does not trigger callback between frames', () async { - var fixture = _Fixture.started(); + test('does not trigger callback between frames', () async { + var fixture = _Fixture.started(); - expect(fixture.calls, 0); - await Future.delayed(const Duration(milliseconds: 100), () async {}); - expect(fixture.calls, 0); - }); + expect(fixture.calls, 0); + await Future.delayed(const Duration(milliseconds: 100), () async {}); + expect(fixture.calls, 0); + }); - test('triggers callback after a frame', () { - var fixture = _Fixture(); - fixture.sut.start(); + test('triggers callback after a frame', () { + var fixture = _Fixture(); + fixture.sut.start(); - expect(fixture.calls, 0); - fixture.drawFrame(); - expect(fixture.calls, 1); - fixture.drawFrame(); - fixture.drawFrame(); - fixture.drawFrame(); - expect(fixture.calls, 4); - }); + expect(fixture.calls, 0); + fixture.drawFrame(); + expect(fixture.calls, 1); + fixture.drawFrame(); + fixture.drawFrame(); + fixture.drawFrame(); + expect(fixture.calls, 4); + }); - test('does not trigger when stopped', () { - var fixture = _Fixture(); - fixture.sut.start(); + test('does not trigger when stopped', () { + var fixture = _Fixture(); + fixture.sut.start(); - expect(fixture.calls, 0); - fixture.drawFrame(); - expect(fixture.calls, 1); - fixture.drawFrame(); - fixture.sut.stop(); - fixture.drawFrame(); - expect(fixture.calls, 2); - }); + expect(fixture.calls, 0); + fixture.drawFrame(); + expect(fixture.calls, 1); + fixture.drawFrame(); + fixture.sut.stop(); + fixture.drawFrame(); + expect(fixture.calls, 2); + }); - test('triggers after a restart', () { - var fixture = _Fixture(); - fixture.sut.start(); + test('triggers after a restart', () { + var fixture = _Fixture(); + fixture.sut.start(); - expect(fixture.calls, 0); - fixture.drawFrame(); - expect(fixture.calls, 1); - fixture.sut.stop(); - fixture.drawFrame(); - expect(fixture.calls, 1); - fixture.sut.start(); - fixture.drawFrame(); - expect(fixture.calls, 2); - }); + expect(fixture.calls, 0); + fixture.drawFrame(); + expect(fixture.calls, 1); + fixture.sut.stop(); + fixture.drawFrame(); + expect(fixture.calls, 1); + fixture.sut.start(); + fixture.drawFrame(); + expect(fixture.calls, 2); }); } From 942044aabbd7f865ec589e3e2d5adcc4fe759dc8 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 7 May 2024 13:19:54 +0200 Subject: [PATCH 50/74] rename recorder_widget_filter.dart --- flutter/lib/src/replay/recorder.dart | 2 +- ..._widget_filter.dart => widget_filter.dart} | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) rename flutter/lib/src/replay/{recorder_widget_filter.dart => widget_filter.dart} (89%) diff --git a/flutter/lib/src/replay/recorder.dart b/flutter/lib/src/replay/recorder.dart index 8b536ffb15..667e789643 100644 --- a/flutter/lib/src/replay/recorder.dart +++ b/flutter/lib/src/replay/recorder.dart @@ -7,7 +7,7 @@ import 'package:meta/meta.dart'; import '../../sentry_flutter.dart'; import 'recorder_config.dart'; -import 'recorder_widget_filter.dart'; +import 'widget_filter.dart'; import 'scheduler.dart'; @internal diff --git a/flutter/lib/src/replay/recorder_widget_filter.dart b/flutter/lib/src/replay/widget_filter.dart similarity index 89% rename from flutter/lib/src/replay/recorder_widget_filter.dart rename to flutter/lib/src/replay/widget_filter.dart index 9075f039e9..452c1ec7db 100644 --- a/flutter/lib/src/replay/recorder_widget_filter.dart +++ b/flutter/lib/src/replay/widget_filter.dart @@ -30,7 +30,10 @@ class WidgetFilter { final widget = element.widget; if (!_isVisible(widget)) { - _devlog("WidgetFilter skipping invisible: $widget"); + assert(() { + logger(SentryLevel.debug, "WidgetFilter skipping invisible: $widget"); + return true; + }()); return; } @@ -77,12 +80,18 @@ class WidgetFilter { ); if (!rect.overlaps(_bounds)) { - _devlog("WidgetFilter skipping offscreen: $widget"); + assert(() { + logger(SentryLevel.debug, "WidgetFilter skipping offscreen: $widget"); + return true; + }()); return false; } items.add(WidgetFilterItem(color ?? _defaultColor, rect)); - _devlog("WidgetFilter obscuring: $widget"); + assert(() { + logger(SentryLevel.debug, "WidgetFilter obscuring: $widget"); + return true; + }()); return true; } @@ -101,15 +110,6 @@ class WidgetFilter { return true; } - // Should be completely trimmed out in production builds. - @pragma('vm:prefer-inline') - void _devlog(String message) { - assert(() { - logger(SentryLevel.debug, message); - return true; - }()); - } - @pragma('vm:prefer-inline') void _cantObscure(Widget widget, String message) { if (!_warnedWidgets.contains(widget)) { From 9efae7b9056689d9f0c3e31b89ffa99de6465ddc Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 7 May 2024 13:23:56 +0200 Subject: [PATCH 51/74] fixup scheduler test --- flutter/test/replay/scheduler_test.dart | 36 +++++++++++++------------ 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/flutter/test/replay/scheduler_test.dart b/flutter/test/replay/scheduler_test.dart index 8352790707..4e851b9927 100644 --- a/flutter/test/replay/scheduler_test.dart +++ b/flutter/test/replay/scheduler_test.dart @@ -7,48 +7,48 @@ void main() { var fixture = _Fixture.started(); expect(fixture.calls, 0); - await Future.delayed(const Duration(milliseconds: 100), () async {}); + await Future.delayed(const Duration(milliseconds: 100), () {}); expect(fixture.calls, 0); }); - test('triggers callback after a frame', () { + test('triggers callback after a frame', () async { var fixture = _Fixture(); fixture.sut.start(); expect(fixture.calls, 0); - fixture.drawFrame(); + await fixture.drawFrame(); expect(fixture.calls, 1); - fixture.drawFrame(); - fixture.drawFrame(); - fixture.drawFrame(); + await fixture.drawFrame(); + await fixture.drawFrame(); + await fixture.drawFrame(); expect(fixture.calls, 4); }); - test('does not trigger when stopped', () { + test('does not trigger when stopped', () async { var fixture = _Fixture(); fixture.sut.start(); expect(fixture.calls, 0); - fixture.drawFrame(); + await fixture.drawFrame(); expect(fixture.calls, 1); - fixture.drawFrame(); + await fixture.drawFrame(); fixture.sut.stop(); - fixture.drawFrame(); + await fixture.drawFrame(); expect(fixture.calls, 2); }); - test('triggers after a restart', () { + test('triggers after a restart', () async { var fixture = _Fixture(); fixture.sut.start(); expect(fixture.calls, 0); - fixture.drawFrame(); + await fixture.drawFrame(); expect(fixture.calls, 1); fixture.sut.stop(); - fixture.drawFrame(); + await fixture.drawFrame(); expect(fixture.calls, 1); fixture.sut.start(); - fixture.drawFrame(); + await fixture.drawFrame(); expect(fixture.calls, 2); }); } @@ -56,7 +56,7 @@ void main() { class _Fixture { var calls = 0; late final Scheduler sut; - late FrameCallback registeredCallback; + FrameCallback? registeredCallback; var _frames = 0; _Fixture() { @@ -73,8 +73,10 @@ class _Fixture { return _Fixture()..sut.start(); } - void drawFrame() { + Future drawFrame() async { + await Future.delayed(const Duration(milliseconds: 8), () {}); _frames++; - registeredCallback(Duration(milliseconds: _frames)); + registeredCallback!(Duration(milliseconds: _frames)); + registeredCallback = null; } } From 35ed86bde8585b7a5ba0892261afd45997eac043 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 7 May 2024 13:28:52 +0200 Subject: [PATCH 52/74] improve test coverage --- flutter/test/replay/scheduler_test.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/flutter/test/replay/scheduler_test.dart b/flutter/test/replay/scheduler_test.dart index 4e851b9927..1f1ce48598 100644 --- a/flutter/test/replay/scheduler_test.dart +++ b/flutter/test/replay/scheduler_test.dart @@ -3,6 +3,13 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:sentry_flutter/src/replay/scheduler.dart'; void main() { + test('defaults to the correct binding', () async { + var factory = () => Scheduler(const Duration(seconds: 1), (_) async {}); + expect(factory, throwsAssertionError); + TestWidgetsFlutterBinding.ensureInitialized(); + expect(factory, isNotNull); + }); + test('does not trigger callback between frames', () async { var fixture = _Fixture.started(); From 63af017b898969b21a97723f50594133fc610cf7 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 7 May 2024 13:30:33 +0200 Subject: [PATCH 53/74] pr cleanup --- .../app/src/main/cpp/native-sample.cpp | 29 +++++++++---------- flutter/example/android/settings.gradle | 10 ------- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/flutter/example/android/app/src/main/cpp/native-sample.cpp b/flutter/example/android/app/src/main/cpp/native-sample.cpp index c84e77da41..deb1bc4e74 100644 --- a/flutter/example/android/app/src/main/cpp/native-sample.cpp +++ b/flutter/example/android/app/src/main/cpp/native-sample.cpp @@ -1,26 +1,25 @@ -#include #include +#include #include #define TAG "sentry-sample" extern "C" { -JNIEXPORT void JNICALL -Java_io_sentry_samples_flutter_MainActivity_crash(JNIEnv *env, jclass cls) { - __android_log_print(ANDROID_LOG_WARN, TAG, - "About to crash with a SEGFAULT in C++!"); - char *ptr = 0; - *ptr += 1; +JNIEXPORT void JNICALL Java_io_sentry_samples_flutter_MainActivity_crash(JNIEnv *env, jclass cls) { + __android_log_print(ANDROID_LOG_WARN, TAG, "About to crash with a SEGFAULT in C++!"); + char *ptr = 0; + *ptr += 1; } -JNIEXPORT void JNICALL -Java_io_sentry_samples_flutter_MainActivity_message(JNIEnv *env, jclass cls) { - // __android_log_print(ANDROID_LOG_WARN, TAG, "Sending message."); - sentry_value_t event = sentry_value_new_message_event( - /* level */ SENTRY_LEVEL_INFO, - /* logger */ "native", - /* message */ "message from C++!"); - sentry_capture_event(event); +JNIEXPORT void JNICALL Java_io_sentry_samples_flutter_MainActivity_message(JNIEnv *env, jclass cls) { + // __android_log_print(ANDROID_LOG_WARN, TAG, "Sending message."); + sentry_value_t event = sentry_value_new_message_event( + /* level */ SENTRY_LEVEL_INFO, + /* logger */ "native", + /* message */ "message from C++!" + ); + sentry_capture_event(event); } + } diff --git a/flutter/example/android/settings.gradle b/flutter/example/android/settings.gradle index 0d19a22bf4..44e62bcf06 100644 --- a/flutter/example/android/settings.gradle +++ b/flutter/example/android/settings.gradle @@ -9,13 +9,3 @@ localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } def flutterSdkPath = properties.getProperty("flutter.sdk") assert flutterSdkPath != null, "flutter.sdk not set in local.properties" apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" - -// includeBuild("../../../../sentry-java") { -// dependencySubstitution { -// substitute(module("io.sentry:sentry")).using(project(":sentry")) -// substitute(module("io.sentry:sentry-android")).using(project(":sentry-android")) -// substitute(module("io.sentry:sentry-android-core")).using(project(":sentry-android-core")) -// substitute(module("io.sentry:sentry-android-ndk")).using(project(":sentry-android-ndk")) -// substitute(module("io.sentry:sentry-replay")).using(project(":sentry-android-replay")) -// } -// } From 12f57749dae1aa6f7e41c397c8a3694c70ff7ed2 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 8 May 2024 10:14:29 +0200 Subject: [PATCH 54/74] test: widget filter --- flutter/lib/src/replay/recorder.dart | 4 +- flutter/lib/src/replay/widget_filter.dart | 11 ++- flutter/test/replay/widget_filter_test.dart | 98 +++++++++++++++++++++ 3 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 flutter/test/replay/widget_filter_test.dart diff --git a/flutter/lib/src/replay/recorder.dart b/flutter/lib/src/replay/recorder.dart index 667e789643..a7967ec671 100644 --- a/flutter/lib/src/replay/recorder.dart +++ b/flutter/lib/src/replay/recorder.dart @@ -76,11 +76,11 @@ class ScreenshotRecorder { final filter = _widgetFilter; if (filter != null) { - filter.setupAndClear( + filter.obscure( + context, pixelRatio, Rect.fromLTWH(0, 0, srcWidth * pixelRatio, srcHeight * pixelRatio), ); - context.visitChildElements(filter.obscure); } final blockingTime = watch.elapsedMilliseconds; diff --git a/flutter/lib/src/replay/widget_filter.dart b/flutter/lib/src/replay/widget_filter.dart index 452c1ec7db..2e593f4fe0 100644 --- a/flutter/lib/src/replay/widget_filter.dart +++ b/flutter/lib/src/replay/widget_filter.dart @@ -20,13 +20,18 @@ class WidgetFilter { required this.redactImages, required this.logger}); - void setupAndClear(double pixelRatio, Rect bounds) { + void obscure(BuildContext context, double pixelRatio, Rect bounds) { _pixelRatio = pixelRatio; _bounds = bounds; items.clear(); + if (context is Element) { + _obscure(context); + } else { + context.visitChildElements(_obscure); + } } - void obscure(Element element) { + void _obscure(Element element) { final widget = element.widget; if (!_isVisible(widget)) { @@ -39,7 +44,7 @@ class WidgetFilter { final obscured = _obscureIfNeeded(element, widget); if (!obscured) { - element.visitChildElements(obscure); + element.visitChildElements(_obscure); } } diff --git a/flutter/test/replay/widget_filter_test.dart b/flutter/test/replay/widget_filter_test.dart new file mode 100644 index 0000000000..81db46454f --- /dev/null +++ b/flutter/test/replay/widget_filter_test.dart @@ -0,0 +1,98 @@ +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:sentry_flutter/src/replay/widget_filter.dart'; + +void main() async { + TestWidgetsFlutterBinding.ensureInitialized(); + const defaultBounds = Rect.fromLTRB(0, 0, 1000, 1000); + + final createSut = + ({bool redactImages = false, bool redactText = false}) => WidgetFilter( + logger: (level, message, {exception, logger, stackTrace}) {}, + redactImages: redactImages, + redactText: redactText, + ); + + group('redact text', () { + testWidgets('redacts the correct number of elements', (tester) async { + final sut = createSut(redactText: true); + final element = await _getTestElement(tester); + sut.obscure(element, 1.0, defaultBounds); + expect(sut.items.length, 2); + }); + + testWidgets('does not redact text when disabled', (tester) async { + final sut = createSut(redactText: false); + final element = await _getTestElement(tester); + sut.obscure(element, 1.0, defaultBounds); + expect(sut.items.length, 0); + }); + + testWidgets('does not redact elements that are outside the screen', + (tester) async { + final sut = createSut(redactText: true); + final element = await _getTestElement(tester); + sut.obscure(element, 1.0, Rect.fromLTRB(0, 0, 100, 100)); + expect(sut.items.length, 1); + }); + }); + + group('redact images', () { + testWidgets('redacts the correct number of elements', (tester) async { + final sut = createSut(redactImages: true); + final element = await _getTestElement(tester); + sut.obscure(element, 1.0, defaultBounds); + expect(sut.items.length, 2); + }); + + testWidgets('does not redact text when disabled', (tester) async { + final sut = createSut(redactImages: false); + final element = await _getTestElement(tester); + sut.obscure(element, 1.0, defaultBounds); + expect(sut.items.length, 0); + }); + + testWidgets('does not redact elements that are outside the screen', + (tester) async { + final sut = createSut(redactImages: true); + final element = await _getTestElement(tester); + sut.obscure(element, 1.0, Rect.fromLTRB(0, 0, 500, 100)); + expect(sut.items.length, 1); + }); + }); +} + +Future _getTestElement(WidgetTester tester) async { + await tester.pumpWidget(MaterialApp( + home: SingleChildScrollView( + child: Column( + children: [ + Image.memory(Uint8List.fromList(_sampleBitmap), width: 1, height: 1), + const Padding( + padding: EdgeInsets.all(15), + child: Center(child: Text('Centered text')), + ), + ElevatedButton( + onPressed: () {}, + child: Text('Button title'), + ), + Image.memory(Uint8List.fromList(_sampleBitmap), width: 1, height: 1), + ], + ), + ), + )); + return TestWidgetsFlutterBinding.instance.rootElement!; +} + +const _sampleBitmap = [ + 66, 77, 142, 0, 0, 0, 0, 0, 0, 0, 138, 0, 0, 0, 124, 0, 0, 0, 1, 0, 0, 0, + 255, 255, 255, 255, 1, 0, 32, 0, 3, 0, 0, 0, 4, 0, 0, 0, 19, 11, 0, 0, 19, + 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 0, + 0, 0, 0, 255, 66, 71, 82, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, + 135, 135, 255, + // This comment prevents dartfmt from splitting the list to many more lines. +]; From 50a13f636692565a0f4a9f40247256f761111b8c Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 8 May 2024 10:50:28 +0200 Subject: [PATCH 55/74] cleanup --- flutter/lib/src/replay/widget_filter.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flutter/lib/src/replay/widget_filter.dart b/flutter/lib/src/replay/widget_filter.dart index 2e593f4fe0..83e069cb97 100644 --- a/flutter/lib/src/replay/widget_filter.dart +++ b/flutter/lib/src/replay/widget_filter.dart @@ -6,14 +6,14 @@ import '../../sentry_flutter.dart'; @internal class WidgetFilter { - static const _defaultColor = Color.fromARGB(255, 0, 0, 0); + final items = []; + final SentryLogger logger; final bool redactText; final bool redactImages; + static const _defaultColor = Color.fromARGB(255, 0, 0, 0); late double _pixelRatio; late Rect _bounds; - final List items = []; - final SentryLogger logger; - final Set _warnedWidgets = {}; + final _warnedWidgets = {}; WidgetFilter( {required this.redactText, @@ -117,8 +117,8 @@ class WidgetFilter { @pragma('vm:prefer-inline') void _cantObscure(Widget widget, String message) { - if (!_warnedWidgets.contains(widget)) { - _warnedWidgets.add(widget); + if (!_warnedWidgets.contains(widget.hashCode)) { + _warnedWidgets.add(widget.hashCode); logger(SentryLevel.warning, "WidgetFilter cannot obscure widget $widget: $message"); } From 725fd02980b5aa6b831780d1280eb0d7255a1f21 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 8 May 2024 10:55:17 +0200 Subject: [PATCH 56/74] test widget filter visibility --- flutter/test/replay/widget_filter_test.dart | 41 ++++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/flutter/test/replay/widget_filter_test.dart b/flutter/test/replay/widget_filter_test.dart index 81db46454f..951e0c4040 100644 --- a/flutter/test/replay/widget_filter_test.dart +++ b/flutter/test/replay/widget_filter_test.dart @@ -65,22 +65,35 @@ void main() async { } Future _getTestElement(WidgetTester tester) async { + final newImage = () => + Image.memory(Uint8List.fromList(_sampleBitmap), width: 1, height: 1); await tester.pumpWidget(MaterialApp( home: SingleChildScrollView( - child: Column( - children: [ - Image.memory(Uint8List.fromList(_sampleBitmap), width: 1, height: 1), - const Padding( - padding: EdgeInsets.all(15), - child: Center(child: Text('Centered text')), - ), - ElevatedButton( - onPressed: () {}, - child: Text('Button title'), - ), - Image.memory(Uint8List.fromList(_sampleBitmap), width: 1, height: 1), - ], - ), + child: Visibility( + visible: true, + child: Opacity( + opacity: 0.5, + child: Column( + children: [ + newImage(), + const Padding( + padding: EdgeInsets.all(15), + child: Center(child: Text('Centered text')), + ), + ElevatedButton( + onPressed: () {}, + child: Text('Button title'), + ), + newImage(), + // Invisible widgets won't be obscured. + Visibility(visible: false, child: Text('Invisible text')), + Visibility(visible: false, child: newImage()), + Opacity(opacity: 0, child: Text('Invisible text')), + Opacity(opacity: 0, child: newImage()), + Offstage(offstage: true, child: Text('Offstage text')), + Offstage(offstage: true, child: newImage()), + ], + ))), ), )); return TestWidgetsFlutterBinding.instance.rootElement!; From 4bda0ab5964e29d33ea4e57e42a676e98f817c91 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 8 May 2024 12:29:50 +0200 Subject: [PATCH 57/74] cleanup --- flutter/test/replay/test_widget.dart | 52 +++++++++++++++++ flutter/test/replay/widget_filter_test.dart | 62 +++------------------ 2 files changed, 60 insertions(+), 54 deletions(-) create mode 100644 flutter/test/replay/test_widget.dart diff --git a/flutter/test/replay/test_widget.dart b/flutter/test/replay/test_widget.dart new file mode 100644 index 0000000000..aa7cc6b088 --- /dev/null +++ b/flutter/test/replay/test_widget.dart @@ -0,0 +1,52 @@ +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +Future getTestElement(WidgetTester tester) async { + await tester.pumpWidget(MaterialApp( + home: SingleChildScrollView( + child: Visibility( + visible: true, + child: Opacity( + opacity: 0.5, + child: Column( + children: [ + newImage(), + const Padding( + padding: EdgeInsets.all(15), + child: Center(child: Text('Centered text')), + ), + ElevatedButton( + onPressed: () {}, + child: Text('Button title'), + ), + newImage(), + // Invisible widgets won't be obscured. + Visibility(visible: false, child: Text('Invisible text')), + Visibility(visible: false, child: newImage()), + Opacity(opacity: 0, child: Text('Invisible text')), + Opacity(opacity: 0, child: newImage()), + Offstage(offstage: true, child: Text('Offstage text')), + Offstage(offstage: true, child: newImage()), + ], + ))), + ), + )); + return TestWidgetsFlutterBinding.instance.rootElement!; +} + +Image newImage() => Image.memory( + Uint8List.fromList([ + 66, 77, 142, 0, 0, 0, 0, 0, 0, 0, 138, 0, 0, 0, 124, 0, 0, 0, 1, 0, + 0, 0, 255, 255, 255, 255, 1, 0, 32, 0, 3, 0, 0, 0, 4, 0, 0, 0, 19, + 11, 0, 0, 19, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, + 255, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 66, 71, 82, 115, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, 135, 135, 255, + // This comment prevents dartfmt reformatting this to single-item lines. + ]), + width: 1, + height: 1, + ); diff --git a/flutter/test/replay/widget_filter_test.dart b/flutter/test/replay/widget_filter_test.dart index 951e0c4040..434ac031ac 100644 --- a/flutter/test/replay/widget_filter_test.dart +++ b/flutter/test/replay/widget_filter_test.dart @@ -1,9 +1,9 @@ -import 'dart:typed_data'; - import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:sentry_flutter/src/replay/widget_filter.dart'; +import 'test_widget.dart'; + void main() async { TestWidgetsFlutterBinding.ensureInitialized(); const defaultBounds = Rect.fromLTRB(0, 0, 1000, 1000); @@ -18,14 +18,14 @@ void main() async { group('redact text', () { testWidgets('redacts the correct number of elements', (tester) async { final sut = createSut(redactText: true); - final element = await _getTestElement(tester); + final element = await getTestElement(tester); sut.obscure(element, 1.0, defaultBounds); expect(sut.items.length, 2); }); testWidgets('does not redact text when disabled', (tester) async { final sut = createSut(redactText: false); - final element = await _getTestElement(tester); + final element = await getTestElement(tester); sut.obscure(element, 1.0, defaultBounds); expect(sut.items.length, 0); }); @@ -33,7 +33,7 @@ void main() async { testWidgets('does not redact elements that are outside the screen', (tester) async { final sut = createSut(redactText: true); - final element = await _getTestElement(tester); + final element = await getTestElement(tester); sut.obscure(element, 1.0, Rect.fromLTRB(0, 0, 100, 100)); expect(sut.items.length, 1); }); @@ -42,14 +42,14 @@ void main() async { group('redact images', () { testWidgets('redacts the correct number of elements', (tester) async { final sut = createSut(redactImages: true); - final element = await _getTestElement(tester); + final element = await getTestElement(tester); sut.obscure(element, 1.0, defaultBounds); expect(sut.items.length, 2); }); testWidgets('does not redact text when disabled', (tester) async { final sut = createSut(redactImages: false); - final element = await _getTestElement(tester); + final element = await getTestElement(tester); sut.obscure(element, 1.0, defaultBounds); expect(sut.items.length, 0); }); @@ -57,55 +57,9 @@ void main() async { testWidgets('does not redact elements that are outside the screen', (tester) async { final sut = createSut(redactImages: true); - final element = await _getTestElement(tester); + final element = await getTestElement(tester); sut.obscure(element, 1.0, Rect.fromLTRB(0, 0, 500, 100)); expect(sut.items.length, 1); }); }); } - -Future _getTestElement(WidgetTester tester) async { - final newImage = () => - Image.memory(Uint8List.fromList(_sampleBitmap), width: 1, height: 1); - await tester.pumpWidget(MaterialApp( - home: SingleChildScrollView( - child: Visibility( - visible: true, - child: Opacity( - opacity: 0.5, - child: Column( - children: [ - newImage(), - const Padding( - padding: EdgeInsets.all(15), - child: Center(child: Text('Centered text')), - ), - ElevatedButton( - onPressed: () {}, - child: Text('Button title'), - ), - newImage(), - // Invisible widgets won't be obscured. - Visibility(visible: false, child: Text('Invisible text')), - Visibility(visible: false, child: newImage()), - Opacity(opacity: 0, child: Text('Invisible text')), - Opacity(opacity: 0, child: newImage()), - Offstage(offstage: true, child: Text('Offstage text')), - Offstage(offstage: true, child: newImage()), - ], - ))), - ), - )); - return TestWidgetsFlutterBinding.instance.rootElement!; -} - -const _sampleBitmap = [ - 66, 77, 142, 0, 0, 0, 0, 0, 0, 0, 138, 0, 0, 0, 124, 0, 0, 0, 1, 0, 0, 0, - 255, 255, 255, 255, 1, 0, 32, 0, 3, 0, 0, 0, 4, 0, 0, 0, 19, 11, 0, 0, 19, - 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 66, 71, 82, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135, - 135, 135, 255, - // This comment prevents dartfmt from splitting the list to many more lines. -]; From afb65f64a3d73cfec2bdec4dc0125e7061df199e Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 8 May 2024 15:07:05 +0200 Subject: [PATCH 58/74] always add screenshot widget --- .../screenshot/sentry_screenshot_widget.dart | 29 ++++--------------- .../screenshot_event_processor_test.dart | 1 - .../sentry_screenshot_widget_test.dart | 1 - 3 files changed, 5 insertions(+), 26 deletions(-) diff --git a/flutter/lib/src/screenshot/sentry_screenshot_widget.dart b/flutter/lib/src/screenshot/sentry_screenshot_widget.dart index e83d46d0c5..6eafb935a5 100644 --- a/flutter/lib/src/screenshot/sentry_screenshot_widget.dart +++ b/flutter/lib/src/screenshot/sentry_screenshot_widget.dart @@ -1,8 +1,6 @@ import 'package:flutter/material.dart'; import 'package:meta/meta.dart'; -import '../../sentry_flutter.dart'; - /// Key which is used to identify the [RepaintBoundary] @internal final sentryScreenshotWidgetGlobalKey = @@ -25,36 +23,19 @@ final sentryScreenshotWidgetGlobalKey = /// times. class SentryScreenshotWidget extends StatefulWidget { final Widget child; - late final Hub _hub; - - SentryFlutterOptions? get _options => - // ignore: invalid_use_of_internal_member - _hub.options is SentryFlutterOptions - // ignore: invalid_use_of_internal_member - ? _hub.options as SentryFlutterOptions - : null; - SentryScreenshotWidget({ - super.key, - required this.child, - @internal Hub? hub, - }) : _hub = hub ?? HubAdapter(); + const SentryScreenshotWidget({super.key, required this.child}); @override _SentryScreenshotWidgetState createState() => _SentryScreenshotWidgetState(); } class _SentryScreenshotWidgetState extends State { - SentryFlutterOptions? get _options => widget._options; - @override Widget build(BuildContext context) { - if (_options?.attachScreenshot ?? false) { - return RepaintBoundary( - key: sentryScreenshotWidgetGlobalKey, - child: widget.child, - ); - } - return widget.child; + return RepaintBoundary( + key: sentryScreenshotWidgetGlobalKey, + child: widget.child, + ); } } diff --git a/flutter/test/event_processor/screenshot_event_processor_test.dart b/flutter/test/event_processor/screenshot_event_processor_test.dart index 2c1d0a4adc..6f46dc312c 100644 --- a/flutter/test/event_processor/screenshot_event_processor_test.dart +++ b/flutter/test/event_processor/screenshot_event_processor_test.dart @@ -33,7 +33,6 @@ void main() { final sut = fixture.getSut(renderer, isWeb); await tester.pumpWidget(SentryScreenshotWidget( - hub: fixture.hub, child: Text('Catching Pokémon is a snap!', textDirection: TextDirection.ltr))); diff --git a/flutter/test/screenshot/sentry_screenshot_widget_test.dart b/flutter/test/screenshot/sentry_screenshot_widget_test.dart index fd4603c794..ecfbc2baab 100644 --- a/flutter/test/screenshot/sentry_screenshot_widget_test.dart +++ b/flutter/test/screenshot/sentry_screenshot_widget_test.dart @@ -63,7 +63,6 @@ class Fixture { hub = Hub(_options); return SentryScreenshotWidget( - hub: hub, child: MaterialApp(home: MyApp()), ); } From f6b9266c6faf56bbde722f27c4539c6fb930fdea Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 8 May 2024 15:18:34 +0200 Subject: [PATCH 59/74] recorder test --- .../src/native/java/sentry_native_java.dart | 3 +- flutter/lib/src/replay/recorder.dart | 9 ++-- flutter/lib/src/replay/scheduler.dart | 8 +--- flutter/test/replay/recorder_test.dart | 43 +++++++++++++++++++ flutter/test/replay/scheduler_test.dart | 9 +--- flutter/test/replay/test_widget.dart | 21 ++++++--- 6 files changed, 66 insertions(+), 27 deletions(-) create mode 100644 flutter/test/replay/recorder_test.dart diff --git a/flutter/lib/src/native/java/sentry_native_java.dart b/flutter/lib/src/native/java/sentry_native_java.dart index 037db9b2a5..0aed976037 100644 --- a/flutter/lib/src/native/java/sentry_native_java.dart +++ b/flutter/lib/src/native/java/sentry_native_java.dart @@ -94,8 +94,7 @@ class SentryNativeJava extends SentryNativeChannel { _replayRecorder = ScreenshotRecorder( config, callback, - _options.logger, - _options.experimental.replay, + _options, )..start(); } } diff --git a/flutter/lib/src/replay/recorder.dart b/flutter/lib/src/replay/recorder.dart index a7967ec671..2ee4504365 100644 --- a/flutter/lib/src/replay/recorder.dart +++ b/flutter/lib/src/replay/recorder.dart @@ -23,10 +23,13 @@ class ScreenshotRecorder { late final Scheduler _scheduler; bool warningLogged = false; - ScreenshotRecorder( - this._config, this._callback, this._logger, this._options) { + ScreenshotRecorder(this._config, this._callback, SentryFlutterOptions options) + : _logger = options.logger, + _options = options.experimental.replay { final frameDuration = Duration(milliseconds: 1000 ~/ _config.frameRate); - _scheduler = Scheduler(frameDuration, _capture); + _scheduler = Scheduler(frameDuration, _capture, + options.bindingUtils.instance!.addPostFrameCallback); + if (_options.redactAllText || _options.redactAllImages) { _widgetFilter = WidgetFilter( redactText: _options.redactAllText, diff --git a/flutter/lib/src/replay/scheduler.dart b/flutter/lib/src/replay/scheduler.dart index 1696882c3b..6ac1841d42 100644 --- a/flutter/lib/src/replay/scheduler.dart +++ b/flutter/lib/src/replay/scheduler.dart @@ -1,4 +1,3 @@ -import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.dart'; import 'package:meta/meta.dart'; @@ -20,12 +19,7 @@ class Scheduler { final void Function(FrameCallback callback) _addPostFrameCallback; - Scheduler(this._interval, this._callback) - : _addPostFrameCallback = RendererBinding.instance.addPostFrameCallback; - - @visibleForTesting - Scheduler.withCustomFrameTiming( - this._interval, this._callback, this._addPostFrameCallback); + Scheduler(this._interval, this._callback, this._addPostFrameCallback); void start() { _running = true; diff --git a/flutter/test/replay/recorder_test.dart b/flutter/test/replay/recorder_test.dart new file mode 100644 index 0000000000..0d7978c490 --- /dev/null +++ b/flutter/test/replay/recorder_test.dart @@ -0,0 +1,43 @@ +import 'dart:ui'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; +import 'package:sentry_flutter/src/replay/recorder.dart'; +import 'package:sentry_flutter/src/replay/recorder_config.dart'; + +import '../mocks.dart'; +import 'test_widget.dart'; + +void main() async { + TestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('captures images', (tester) async { + await tester.runAsync(() async { + await getTestElement(tester); + final fixture = _Fixture(); + expect(fixture.capturedImages, isEmpty); + await tester.pumpAndSettle(const Duration(seconds: 1)); + expect(fixture.capturedImages, ['1000x750']); + }); + }); +} + +class _Fixture { + late final ScreenshotRecorder sut; + final capturedImages = []; + + _Fixture() { + sut = ScreenshotRecorder( + ScreenshotRecorderConfig( + width: 1000, + height: 1000, + frameRate: 1000, + ), + (Image image) async { + capturedImages.add("${image.width}x${image.height}"); + }, + SentryFlutterOptions()..bindingUtils = TestBindingWrapper(), + ); + sut.start(); + } +} diff --git a/flutter/test/replay/scheduler_test.dart b/flutter/test/replay/scheduler_test.dart index 1f1ce48598..adf4c5d5f7 100644 --- a/flutter/test/replay/scheduler_test.dart +++ b/flutter/test/replay/scheduler_test.dart @@ -3,13 +3,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:sentry_flutter/src/replay/scheduler.dart'; void main() { - test('defaults to the correct binding', () async { - var factory = () => Scheduler(const Duration(seconds: 1), (_) async {}); - expect(factory, throwsAssertionError); - TestWidgetsFlutterBinding.ensureInitialized(); - expect(factory, isNotNull); - }); - test('does not trigger callback between frames', () async { var fixture = _Fixture.started(); @@ -67,7 +60,7 @@ class _Fixture { var _frames = 0; _Fixture() { - sut = Scheduler.withCustomFrameTiming( + sut = Scheduler( const Duration(milliseconds: 1), (_) async => calls++, (FrameCallback callback, {String debugLabel = 'callback'}) { diff --git a/flutter/test/replay/test_widget.dart b/flutter/test/replay/test_widget.dart index aa7cc6b088..4b489f5f67 100644 --- a/flutter/test/replay/test_widget.dart +++ b/flutter/test/replay/test_widget.dart @@ -2,13 +2,16 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; Future getTestElement(WidgetTester tester) async { - await tester.pumpWidget(MaterialApp( - home: SingleChildScrollView( - child: Visibility( - visible: true, - child: Opacity( + await tester.pumpWidget( + MaterialApp( + home: SentryWidget( + child: SingleChildScrollView( + child: Visibility( + visible: true, + child: Opacity( opacity: 0.5, child: Column( children: [ @@ -30,9 +33,13 @@ Future getTestElement(WidgetTester tester) async { Offstage(offstage: true, child: Text('Offstage text')), Offstage(offstage: true, child: newImage()), ], - ))), + ), + ), + ), + ), + ), ), - )); + ); return TestWidgetsFlutterBinding.instance.rootElement!; } From 5dc125568f5cc8da0f1e5376fbb998752f7619d1 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 8 May 2024 15:46:55 +0200 Subject: [PATCH 60/74] cleanup --- flutter/scripts/generate-android-bindings.sh | 19 ------------------- flutter/scripts/update-android.sh | 1 - 2 files changed, 20 deletions(-) delete mode 100755 flutter/scripts/generate-android-bindings.sh diff --git a/flutter/scripts/generate-android-bindings.sh b/flutter/scripts/generate-android-bindings.sh deleted file mode 100755 index 88fe2ece05..0000000000 --- a/flutter/scripts/generate-android-bindings.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -if [[ -n ${CI:+x} ]]; then - echo "Running in CI so we need to set up Flutter SDK first" - curl -Lv https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_3.13.3-stable.zip --output /tmp/flutter.zip - unzip -q /tmp/flutter.zip -d /tmp - export PATH=":/tmp/flutter/bin:$PATH" - which flutter - flutter --version -fi - -# jnigen requires building the app first -cd $(dirname "$0")/../ -pushd example -flutter build apk -popd - -dart run jnigen --config ./ffi-jni.yaml diff --git a/flutter/scripts/update-android.sh b/flutter/scripts/update-android.sh index 60775a6eec..fc1f3460bd 100755 --- a/flutter/scripts/update-android.sh +++ b/flutter/scripts/update-android.sh @@ -20,7 +20,6 @@ get-repo) set-version) newValue="${BASH_REMATCH[1]}$2" echo "${content/${BASH_REMATCH[0]}/$newValue}" >$file - ../scripts/generate-android-bindings.sh "$2" ;; *) echo "Unknown argument $1" From 225c0c0110ef243d4bf23aea510286add648ac64 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 8 May 2024 16:26:32 +0200 Subject: [PATCH 61/74] limit recorder test to vm --- flutter/test/replay/recorder_test.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/flutter/test/replay/recorder_test.dart b/flutter/test/replay/recorder_test.dart index 0d7978c490..bbc3441a1c 100644 --- a/flutter/test/replay/recorder_test.dart +++ b/flutter/test/replay/recorder_test.dart @@ -1,3 +1,7 @@ +// For some reason, this test is not working in the browser but that's OK, we +// don't support video recording anyway. +@TestOn('vm') + import 'dart:ui'; import 'package:flutter_test/flutter_test.dart'; From 46527a39287324c1469263170d95c1eef335c772 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 May 2024 19:35:37 +0200 Subject: [PATCH 62/74] wip: integration test --- flutter/example/integration_test/all.dart | 2 + .../example/integration_test/replay_test.dart | 39 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 flutter/example/integration_test/replay_test.dart diff --git a/flutter/example/integration_test/all.dart b/flutter/example/integration_test/all.dart index 69cc5a6641..77a4b2923d 100644 --- a/flutter/example/integration_test/all.dart +++ b/flutter/example/integration_test/all.dart @@ -1,8 +1,10 @@ // Workaround for https://github.com/flutter/flutter/issues/101031 import 'integration_test.dart' as a; import 'profiling_test.dart' as b; +import 'replay_test.dart' as c; void main() { a.main(); b.main(); + c.main(); } diff --git a/flutter/example/integration_test/replay_test.dart b/flutter/example/integration_test/replay_test.dart new file mode 100644 index 0000000000..00d85ca14f --- /dev/null +++ b/flutter/example/integration_test/replay_test.dart @@ -0,0 +1,39 @@ +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; + +void main() { + group('Replay recording', () { + setUp(() async { + await SentryFlutter.init((options) { + // ignore: invalid_use_of_internal_member + options.automatedTestMode = true; + options.dsn = 'https://abc@def.ingest.sentry.io/1234567'; + options.debug = true; + options.experimental.replay.sessionSampleRate = 1.0; + }); + }); + + tearDown(() async { + await Sentry.close(); + }); + + test('native binding is initialized', () async { + // ignore: invalid_use_of_internal_member + expect(SentryFlutter.native, isNotNull); + }); + + test('session replay is captured', () async { + // TODO add when the beforeSend callback is implemented for replays. + }); + + test('replay is captured on errors', () async { + // TODO we may need an HTTP server for this because Android sends replays + // in a separate envelope. + }); + }, + skip: Platform.isAndroid + ? false + : "Replay recording is not supported on this platform"); +} From 0bc8fff762b4f5bea88cbf98781a419caa8d5ba2 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 9 May 2024 20:05:22 +0200 Subject: [PATCH 63/74] cleanup --- flutter/android/build.gradle | 1 - .../io/sentry/flutter/SentryFlutterPlugin.kt | 6 +-- .../flutter/SentryFlutterReplayRecorder.kt | 54 +++++++++++++------ 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/flutter/android/build.gradle b/flutter/android/build.gradle index e962963221..551819a9fb 100644 --- a/flutter/android/build.gradle +++ b/flutter/android/build.gradle @@ -61,7 +61,6 @@ android { dependencies { api 'io.sentry:sentry-android:7.9.0-alpha.1' - api 'io.sentry:sentry-android-replay:7.9.0-alpha.1' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" // Required -- JUnit 4 framework diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index 16109543e2..55e7314c57 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -354,9 +354,9 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } val args = call.arguments() as List? ?: listOf() if (args.isNotEmpty()) { - val event = args.first() as ByteArray? - if (event != null && event.isNotEmpty()) { - val id = InternalSentrySdk.captureEnvelope(event) + val envelopeData = args.first() as ByteArray? + if (envelopeData != null && envelopeData.isNotEmpty()) { + val id = InternalSentrySdk.captureEnvelope(envelopeData) if (id != null) { result.success("") } else { diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt index 34cf1404c5..357647158c 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt @@ -2,45 +2,67 @@ package io.sentry.flutter import android.os.Handler import android.os.Looper +import android.util.Log import io.flutter.plugin.common.MethodChannel import io.sentry.android.replay.Recorder import io.sentry.android.replay.ReplayIntegration import io.sentry.android.replay.ScreenshotRecorderConfig -// TODO try, catch, and log errors, otherwise the app will crash internal class SentryFlutterReplayRecorder( - private val channel: MethodChannel, - private val integration: ReplayIntegration, + private val channel: MethodChannel, + private val integration: ReplayIntegration, ) : Recorder { override fun start(config: ScreenshotRecorderConfig) { val cacheDirPath = integration.replayCacheDir?.absolutePath if (cacheDirPath == null) { - // TODO debug log + Log.w("Sentry", "Replay cache directory is null, can't start replay recorder.") return } Handler(Looper.getMainLooper()).post { - channel.invokeMethod( - "ReplayRecorder.start", - mapOf( - "directory" to cacheDirPath, - "width" to config.recordingWidth, - "height" to config.recordingHeight, - "frameRate" to config.frameRate, - ), - ) + try { + channel.invokeMethod( + "ReplayRecorder.start", + mapOf( + "directory" to cacheDirPath, + "width" to config.recordingWidth, + "height" to config.recordingHeight, + "frameRate" to config.frameRate, + ), + ) + } catch (e: Exception) { + Log.w("Sentry", "Failed to start replay recorder", e) + } } } override fun resume() { - Handler(Looper.getMainLooper()).post { channel.invokeMethod("ReplayRecorder.resume", null) } + Handler(Looper.getMainLooper()).post { + try { + channel.invokeMethod("ReplayRecorder.resume", null) + } catch (e: Exception) { + Log.w("Sentry", "Failed to resume replay recorder", e) + } + } } override fun pause() { - Handler(Looper.getMainLooper()).post { channel.invokeMethod("ReplayRecorder.pause", null) } + Handler(Looper.getMainLooper()).post { + try { + channel.invokeMethod("ReplayRecorder.pause", null) + } catch (e: Exception) { + Log.w("Sentry", "Failed to pause replay recorder", e) + } + } } override fun stop() { - Handler(Looper.getMainLooper()).post { channel.invokeMethod("ReplayRecorder.stop", null) } + Handler(Looper.getMainLooper()).post { + try { + channel.invokeMethod("ReplayRecorder.stop", null) + } catch (e: Exception) { + Log.w("Sentry", "Failed to stop replay recorder", e) + } + } } override fun close() { From 81f4689eaa71c34e12c501c3ab9d60ee28caaa8f Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Sun, 12 May 2024 12:38:48 +0200 Subject: [PATCH 64/74] ktlint format --- .../flutter/SentryFlutterReplayRecorder.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt index 357647158c..02b80b3cd3 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt @@ -9,8 +9,8 @@ import io.sentry.android.replay.ReplayIntegration import io.sentry.android.replay.ScreenshotRecorderConfig internal class SentryFlutterReplayRecorder( - private val channel: MethodChannel, - private val integration: ReplayIntegration, + private val channel: MethodChannel, + private val integration: ReplayIntegration, ) : Recorder { override fun start(config: ScreenshotRecorderConfig) { val cacheDirPath = integration.replayCacheDir?.absolutePath @@ -21,13 +21,13 @@ internal class SentryFlutterReplayRecorder( Handler(Looper.getMainLooper()).post { try { channel.invokeMethod( - "ReplayRecorder.start", - mapOf( - "directory" to cacheDirPath, - "width" to config.recordingWidth, - "height" to config.recordingHeight, - "frameRate" to config.frameRate, - ), + "ReplayRecorder.start", + mapOf( + "directory" to cacheDirPath, + "width" to config.recordingWidth, + "height" to config.recordingHeight, + "frameRate" to config.frameRate, + ), ) } catch (e: Exception) { Log.w("Sentry", "Failed to start replay recorder", e) From 0f6764b03ff80feb8f29fb8ecf5770bfd9465bf8 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Sun, 12 May 2024 12:40:10 +0200 Subject: [PATCH 65/74] detekt suppression --- .../main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt index 02b80b3cd3..cf19b3e675 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt @@ -8,6 +8,7 @@ import io.sentry.android.replay.Recorder import io.sentry.android.replay.ReplayIntegration import io.sentry.android.replay.ScreenshotRecorderConfig +@Suppress("TooGenericExceptionThrown") internal class SentryFlutterReplayRecorder( private val channel: MethodChannel, private val integration: ReplayIntegration, From d35f630f1e111db7473965b7fcf28eb00b6b6a4b Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Sun, 12 May 2024 13:07:19 +0200 Subject: [PATCH 66/74] ktlint format --- .../main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index 0894ba53d1..883d582ab3 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -178,10 +178,10 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { val item = mutableMapOf( - "pluginRegistrationTime" to pluginRegistrationTime, - "appStartTime" to appStartTimeMillis, - "isColdStart" to isColdStart, - ) + "pluginRegistrationTime" to pluginRegistrationTime, + "appStartTime" to appStartTimeMillis, + "isColdStart" to isColdStart, + ) val androidNativeSpans = mutableMapOf() From fee95803e97de77876e79597c724cf83c065874c Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Sun, 12 May 2024 13:52:35 +0200 Subject: [PATCH 67/74] improve scheduler stop behavior --- .../src/native/java/sentry_native_java.dart | 4 +-- flutter/lib/src/replay/recorder.dart | 10 +++--- flutter/lib/src/replay/scheduler.dart | 18 +++++----- flutter/test/replay/recorder_test.dart | 33 ++++++++++++++----- flutter/test/replay/scheduler_test.dart | 4 +-- flutter/test/replay/test_widget.dart | 2 +- flutter/test/replay/widget_filter_test.dart | 12 +++---- 7 files changed, 49 insertions(+), 34 deletions(-) diff --git a/flutter/lib/src/native/java/sentry_native_java.dart b/flutter/lib/src/native/java/sentry_native_java.dart index 0aed976037..2aa66f6a98 100644 --- a/flutter/lib/src/native/java/sentry_native_java.dart +++ b/flutter/lib/src/native/java/sentry_native_java.dart @@ -36,11 +36,11 @@ class SentryNativeJava extends SentryNativeChannel { ); break; case 'ReplayRecorder.stop': - _replayRecorder?.stop(); + await _replayRecorder?.stop(); _replayRecorder = null; break; case 'ReplayRecorder.pause': - _replayRecorder?.stop(); + await _replayRecorder?.stop(); break; case 'ReplayRecorder.resume': _replayRecorder?.start(); diff --git a/flutter/lib/src/replay/recorder.dart b/flutter/lib/src/replay/recorder.dart index 2ee4504365..a3b096c8da 100644 --- a/flutter/lib/src/replay/recorder.dart +++ b/flutter/lib/src/replay/recorder.dart @@ -43,15 +43,14 @@ class ScreenshotRecorder { _scheduler.start(); } - void stop() { - _scheduler.stop(); + Future stop() async { + await _scheduler.stop(); _logger(SentryLevel.debug, "Replay: replay capture stopped."); } Future _capture(Duration sinceSchedulerEpoch) async { final context = sentryScreenshotWidgetGlobalKey.currentContext; final renderObject = context?.findRenderObject() as RenderRepaintBoundary?; - if (context == null || renderObject == null) { if (!warningLogged) { _logger( @@ -70,9 +69,8 @@ class ScreenshotRecorder { // rounded to next multitude of 16. Therefore, we scale the image. final srcWidth = renderObject.size.width; final srcHeight = renderObject.size.height; - final pixelRatioX = _config.width / srcWidth; - final pixelRatioY = _config.height / srcHeight; - final pixelRatio = min(pixelRatioY, pixelRatioX); + final pixelRatio = + min(_config.width / srcWidth, _config.height / srcHeight); // First, we synchronously capture the image and enumarete widgets on the main UI loop. final futureImage = renderObject.toImage(pixelRatio: pixelRatio); diff --git a/flutter/lib/src/replay/scheduler.dart b/flutter/lib/src/replay/scheduler.dart index 6ac1841d42..4d246360e3 100644 --- a/flutter/lib/src/replay/scheduler.dart +++ b/flutter/lib/src/replay/scheduler.dart @@ -15,7 +15,7 @@ class Scheduler { final SchedulerCallback _callback; final Duration _interval; bool _running = false; - bool _scheduled = false; + Future? _scheduled; final void Function(FrameCallback callback) _addPostFrameCallback; @@ -23,30 +23,32 @@ class Scheduler { void start() { _running = true; - if (!_scheduled) { + if (_scheduled == null) { _runAfterNextFrame(); } } - void stop() { + Future stop() async { _running = false; + final scheduled = _scheduled; + _scheduled = null; + if (scheduled != null) { + await scheduled; + } } @pragma('vm:prefer-inline') void _scheduleNext() { - if (!_scheduled) { - _scheduled = true; - Future.delayed(_interval, _runAfterNextFrame); - } + _scheduled ??= Future.delayed(_interval, _runAfterNextFrame); } @pragma('vm:prefer-inline') void _runAfterNextFrame() { + _scheduled = null; _addPostFrameCallback(_run); } void _run(Duration sinceSchedulerEpoch) { - _scheduled = false; if (!_running) return; _callback(sinceSchedulerEpoch).then((_) => _scheduleNext()); } diff --git a/flutter/test/replay/recorder_test.dart b/flutter/test/replay/recorder_test.dart index bbc3441a1c..516880a2c6 100644 --- a/flutter/test/replay/recorder_test.dart +++ b/flutter/test/replay/recorder_test.dart @@ -16,21 +16,25 @@ void main() async { TestWidgetsFlutterBinding.ensureInitialized(); testWidgets('captures images', (tester) async { - await tester.runAsync(() async { - await getTestElement(tester); - final fixture = _Fixture(); - expect(fixture.capturedImages, isEmpty); - await tester.pumpAndSettle(const Duration(seconds: 1)); - expect(fixture.capturedImages, ['1000x750']); - }); + final fixture = await _Fixture.create(tester); + expect(fixture.capturedImages, isEmpty); + await fixture.nextFrame(); + expect(fixture.capturedImages, ['1000x750']); + await fixture.nextFrame(); + expect(fixture.capturedImages, ['1000x750', '1000x750']); + final stopFuture = fixture.sut.stop(); + await fixture.nextFrame(); + await stopFuture; + expect(fixture.capturedImages, ['1000x750', '1000x750']); }); } class _Fixture { + final WidgetTester _tester; late final ScreenshotRecorder sut; final capturedImages = []; - _Fixture() { + _Fixture._(this._tester) { sut = ScreenshotRecorder( ScreenshotRecorderConfig( width: 1000, @@ -42,6 +46,17 @@ class _Fixture { }, SentryFlutterOptions()..bindingUtils = TestBindingWrapper(), ); - sut.start(); + } + + static Future<_Fixture> create(WidgetTester tester) async { + final fixture = _Fixture._(tester); + await pumpTestElement(tester); + fixture.sut.start(); + return fixture; + } + + Future nextFrame() async { + _tester.binding.scheduleFrame(); + await _tester.pumpAndSettle(const Duration(seconds: 1)); } } diff --git a/flutter/test/replay/scheduler_test.dart b/flutter/test/replay/scheduler_test.dart index adf4c5d5f7..c41260c854 100644 --- a/flutter/test/replay/scheduler_test.dart +++ b/flutter/test/replay/scheduler_test.dart @@ -32,7 +32,7 @@ void main() { await fixture.drawFrame(); expect(fixture.calls, 1); await fixture.drawFrame(); - fixture.sut.stop(); + await fixture.sut.stop(); await fixture.drawFrame(); expect(fixture.calls, 2); }); @@ -44,7 +44,7 @@ void main() { expect(fixture.calls, 0); await fixture.drawFrame(); expect(fixture.calls, 1); - fixture.sut.stop(); + await fixture.sut.stop(); await fixture.drawFrame(); expect(fixture.calls, 1); fixture.sut.start(); diff --git a/flutter/test/replay/test_widget.dart b/flutter/test/replay/test_widget.dart index 4b489f5f67..e85dfacaf8 100644 --- a/flutter/test/replay/test_widget.dart +++ b/flutter/test/replay/test_widget.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -Future getTestElement(WidgetTester tester) async { +Future pumpTestElement(WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: SentryWidget( diff --git a/flutter/test/replay/widget_filter_test.dart b/flutter/test/replay/widget_filter_test.dart index 434ac031ac..3e17f2b5b6 100644 --- a/flutter/test/replay/widget_filter_test.dart +++ b/flutter/test/replay/widget_filter_test.dart @@ -18,14 +18,14 @@ void main() async { group('redact text', () { testWidgets('redacts the correct number of elements', (tester) async { final sut = createSut(redactText: true); - final element = await getTestElement(tester); + final element = await pumpTestElement(tester); sut.obscure(element, 1.0, defaultBounds); expect(sut.items.length, 2); }); testWidgets('does not redact text when disabled', (tester) async { final sut = createSut(redactText: false); - final element = await getTestElement(tester); + final element = await pumpTestElement(tester); sut.obscure(element, 1.0, defaultBounds); expect(sut.items.length, 0); }); @@ -33,7 +33,7 @@ void main() async { testWidgets('does not redact elements that are outside the screen', (tester) async { final sut = createSut(redactText: true); - final element = await getTestElement(tester); + final element = await pumpTestElement(tester); sut.obscure(element, 1.0, Rect.fromLTRB(0, 0, 100, 100)); expect(sut.items.length, 1); }); @@ -42,14 +42,14 @@ void main() async { group('redact images', () { testWidgets('redacts the correct number of elements', (tester) async { final sut = createSut(redactImages: true); - final element = await getTestElement(tester); + final element = await pumpTestElement(tester); sut.obscure(element, 1.0, defaultBounds); expect(sut.items.length, 2); }); testWidgets('does not redact text when disabled', (tester) async { final sut = createSut(redactImages: false); - final element = await getTestElement(tester); + final element = await pumpTestElement(tester); sut.obscure(element, 1.0, defaultBounds); expect(sut.items.length, 0); }); @@ -57,7 +57,7 @@ void main() async { testWidgets('does not redact elements that are outside the screen', (tester) async { final sut = createSut(redactImages: true); - final element = await getTestElement(tester); + final element = await pumpTestElement(tester); sut.obscure(element, 1.0, Rect.fromLTRB(0, 0, 500, 100)); expect(sut.items.length, 1); }); From 8be8d2081f7f0d4be76c4aa386b2f38bf6e0e1b9 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 13 May 2024 20:07:26 +0200 Subject: [PATCH 68/74] wip: error replay mapping --- .../io/sentry/flutter/SentryFlutterPlugin.kt | 14 +++++++++++ .../replay_event_processor.dart | 24 +++++++++++++++++++ .../src/native/java/sentry_native_java.dart | 4 ++++ .../lib/src/native/sentry_native_binding.dart | 2 ++ .../lib/src/native/sentry_native_channel.dart | 7 ++++++ flutter/test/mocks.dart | 7 ++++++ 6 files changed, 58 insertions(+) create mode 100644 flutter/lib/src/event_processor/replay_event_processor.dart diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index 883d582ab3..76ba9645e8 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -81,6 +81,7 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { "removeTag" -> removeTag(call.argument("key"), result) "loadContexts" -> loadContexts(result) "addReplayScreenshot" -> addReplayScreenshot(call.argument("path"), call.argument("timestamp"), result) + "sendReplayForEvent" -> sendReplayForEvent(call.argument("eventId"), call.argument("isCrash"), result) else -> result.notImplemented() } } @@ -527,4 +528,17 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { replay.onScreenshotRecorded(File(path), timestamp) result.success("") } + + private fun sendReplayForEvent( + eventId: String?, + isCrash: Boolean?, + result: Result, + ) { + if (eventId == null || isCrash == null) { + result.error("5", "Arguments are null", null) + return + } + replay.sendReplay(isCrash, eventId, null) + result.success(replay.getReplayId().toString()) + } } diff --git a/flutter/lib/src/event_processor/replay_event_processor.dart b/flutter/lib/src/event_processor/replay_event_processor.dart new file mode 100644 index 0000000000..65ed67141c --- /dev/null +++ b/flutter/lib/src/event_processor/replay_event_processor.dart @@ -0,0 +1,24 @@ +import 'dart:async'; + +import 'package:sentry/sentry.dart'; + +import '../native/sentry_native_binding.dart'; + +class ReplayEventProcessor implements EventProcessor { + final SentryNativeBinding _binding; + + ReplayEventProcessor(this._binding); + + @override + Future apply(SentryEvent event, Hint hint) async { + if (event.eventId != SentryId.empty() && + event.exceptions?.isNotEmpty == true) { + final isCrash = event.exceptions! + .any((element) => element.mechanism?.handled == false); + // ignore: unused_local_variable + final replayId = + await _binding.sendReplayForEvent(event.eventId, isCrash); + } + return event; + } +} diff --git a/flutter/lib/src/native/java/sentry_native_java.dart b/flutter/lib/src/native/java/sentry_native_java.dart index 2aa66f6a98..258036be1d 100644 --- a/flutter/lib/src/native/java/sentry_native_java.dart +++ b/flutter/lib/src/native/java/sentry_native_java.dart @@ -5,6 +5,7 @@ import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; import '../../../sentry_flutter.dart'; +import '../../event_processor/replay_event_processor.dart'; import '../../replay/recorder.dart'; import '../../replay/recorder_config.dart'; import '../sentry_native_channel.dart'; @@ -23,6 +24,9 @@ class SentryNativeJava extends SentryNativeChannel { // conditionally. This allows Dart to trim the code. if (options.experimental.replay.isEnabled) { _options = options; + if ((options.experimental.replay.errorSampleRate ?? 0) > 0) { + options.addEventProcessor(ReplayEventProcessor(this)); + } channel.setMethodCallHandler((call) async { switch (call.method) { case 'ReplayRecorder.start': diff --git a/flutter/lib/src/native/sentry_native_binding.dart b/flutter/lib/src/native/sentry_native_binding.dart index 950e7f9994..6436484ea4 100644 --- a/flutter/lib/src/native/sentry_native_binding.dart +++ b/flutter/lib/src/native/sentry_native_binding.dart @@ -43,4 +43,6 @@ abstract class SentryNativeBinding { Future?> collectProfile( SentryId traceId, int startTimeNs, int endTimeNs); + + Future sendReplayForEvent(SentryId eventId, bool isCrash); } diff --git a/flutter/lib/src/native/sentry_native_channel.dart b/flutter/lib/src/native/sentry_native_channel.dart index b3ddabbf2a..c88ffcee71 100644 --- a/flutter/lib/src/native/sentry_native_channel.dart +++ b/flutter/lib/src/native/sentry_native_channel.dart @@ -151,4 +151,11 @@ class SentryNativeChannel implements SentryNativeBinding { 'startTime': startTimeNs, 'endTime': endTimeNs, }); + + @override + Future sendReplayForEvent(SentryId eventId, bool isCrash) => + channel.invokeMethod('sendReplayForEvent', { + 'eventId': eventId.toString(), + 'isCrash': isCrash, + }).then((value) => SentryId.fromId(value as String)); } diff --git a/flutter/test/mocks.dart b/flutter/test/mocks.dart index a373ee7511..2cc2d6e521 100644 --- a/flutter/test/mocks.dart +++ b/flutter/test/mocks.dart @@ -334,6 +334,7 @@ class MockNativeChannel implements SentryNativeBinding { int numberOfCollectProfileCalls = 0; int numberOfInitCalls = 0; int numberOfCloseCalls = 0; + int numberOfSendReplayForEventCalls = 0; @override Future fetchNativeAppStart() async => nativeAppStart; @@ -425,6 +426,12 @@ class MockNativeChannel implements SentryNativeBinding { numberOfCloseCalls++; return Future.value(null); } + + @override + Future sendReplayForEvent(SentryId eventId, bool isCrash) { + numberOfSendReplayForEventCalls++; + return Future.value(SentryId.newId()); + } } class MockRendererWrapper implements RendererWrapper { From f3057cd6f84fd730e8b08847ccd9e677cb4a84d6 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Mon, 13 May 2024 20:12:06 +0200 Subject: [PATCH 69/74] suppress detekt TooGenericExceptionThrown --- .../flutter/SentryFlutterReplayRecorder.kt | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt index cf19b3e675..3d57b12d77 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt @@ -8,7 +8,6 @@ import io.sentry.android.replay.Recorder import io.sentry.android.replay.ReplayIntegration import io.sentry.android.replay.ScreenshotRecorderConfig -@Suppress("TooGenericExceptionThrown") internal class SentryFlutterReplayRecorder( private val channel: MethodChannel, private val integration: ReplayIntegration, @@ -30,8 +29,8 @@ internal class SentryFlutterReplayRecorder( "frameRate" to config.frameRate, ), ) - } catch (e: Exception) { - Log.w("Sentry", "Failed to start replay recorder", e) + } catch (ignored: Exception) { + Log.w("Sentry", "Failed to start replay recorder", ignored) } } } @@ -40,8 +39,8 @@ internal class SentryFlutterReplayRecorder( Handler(Looper.getMainLooper()).post { try { channel.invokeMethod("ReplayRecorder.resume", null) - } catch (e: Exception) { - Log.w("Sentry", "Failed to resume replay recorder", e) + } catch (ignored: Exception) { + Log.w("Sentry", "Failed to resume replay recorder", ignored) } } } @@ -50,8 +49,8 @@ internal class SentryFlutterReplayRecorder( Handler(Looper.getMainLooper()).post { try { channel.invokeMethod("ReplayRecorder.pause", null) - } catch (e: Exception) { - Log.w("Sentry", "Failed to pause replay recorder", e) + } catch (ignored: Exception) { + Log.w("Sentry", "Failed to pause replay recorder", ignored) } } } @@ -60,8 +59,8 @@ internal class SentryFlutterReplayRecorder( Handler(Looper.getMainLooper()).post { try { channel.invokeMethod("ReplayRecorder.stop", null) - } catch (e: Exception) { - Log.w("Sentry", "Failed to stop replay recorder", e) + } catch (ignored: Exception) { + Log.w("Sentry", "Failed to stop replay recorder", ignored) } } } From 943aceaf7aa2308d09395c418290919a90fd388c Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Tue, 14 May 2024 14:36:43 +0200 Subject: [PATCH 70/74] Update flutter/lib/src/replay/recorder.dart Co-authored-by: Giancarlo Buenaflor --- flutter/lib/src/replay/recorder.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/src/replay/recorder.dart b/flutter/lib/src/replay/recorder.dart index a3b096c8da..ff64711d05 100644 --- a/flutter/lib/src/replay/recorder.dart +++ b/flutter/lib/src/replay/recorder.dart @@ -72,7 +72,7 @@ class ScreenshotRecorder { final pixelRatio = min(_config.width / srcWidth, _config.height / srcHeight); - // First, we synchronously capture the image and enumarete widgets on the main UI loop. + // First, we synchronously capture the image and enumerate widgets on the main UI loop. final futureImage = renderObject.toImage(pixelRatio: pixelRatio); final filter = _widgetFilter; From 0d82f13ae704ea210a5d499f01680d171b8bb80b Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Tue, 14 May 2024 14:36:51 +0200 Subject: [PATCH 71/74] Update flutter/lib/src/native/java/sentry_native_java.dart Co-authored-by: Giancarlo Buenaflor --- flutter/lib/src/native/java/sentry_native_java.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter/lib/src/native/java/sentry_native_java.dart b/flutter/lib/src/native/java/sentry_native_java.dart index 258036be1d..26e3662351 100644 --- a/flutter/lib/src/native/java/sentry_native_java.dart +++ b/flutter/lib/src/native/java/sentry_native_java.dart @@ -69,8 +69,8 @@ class SentryNativeJava extends SentryNativeChannel { ScreenshotRecorderCallback callback = (image) async { var imageData = await image.toByteData(format: ImageByteFormat.png); if (imageData != null) { - var timestamp = DateTime.now().millisecondsSinceEpoch; - var filePath = path.join(cacheDir, "$timestamp.png"); + final timestamp = DateTime.now().millisecondsSinceEpoch; + final filePath = path.join(cacheDir, "$timestamp.png"); _options.logger( SentryLevel.debug, From 49d4239c739e26fe00e9febda17ecf3d2f83939c Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Tue, 14 May 2024 14:46:46 +0200 Subject: [PATCH 72/74] improve comments --- .../src/native/java/sentry_native_java.dart | 7 +++++-- flutter/lib/src/sentry_replay_options.dart | 20 ++++++------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/flutter/lib/src/native/java/sentry_native_java.dart b/flutter/lib/src/native/java/sentry_native_java.dart index 26e3662351..241957e5d7 100644 --- a/flutter/lib/src/native/java/sentry_native_java.dart +++ b/flutter/lib/src/native/java/sentry_native_java.dart @@ -20,13 +20,16 @@ class SentryNativeJava extends SentryNativeChannel { @override Future init(SentryFlutterOptions options) async { - // We only need these when replay is enabled so let's set it up - // conditionally. This allows Dart to trim the code. + // We only need these when replay is enabled (session or error capture) + // so let's set it up conditionally. This allows Dart to trim the code. if (options.experimental.replay.isEnabled) { _options = options; + + // We only need the integration when error-replay capture is enabled. if ((options.experimental.replay.errorSampleRate ?? 0) > 0) { options.addEventProcessor(ReplayEventProcessor(this)); } + channel.setMethodCallHandler((call) async { switch (call.method) { case 'ReplayRecorder.start': diff --git a/flutter/lib/src/sentry_replay_options.dart b/flutter/lib/src/sentry_replay_options.dart index 838d053ce2..e98aed7418 100644 --- a/flutter/lib/src/sentry_replay_options.dart +++ b/flutter/lib/src/sentry_replay_options.dart @@ -4,14 +4,10 @@ import 'package:meta/meta.dart'; class SentryReplayOptions { double? _sessionSampleRate; - /// Indicates the percentage in which the replay for the session will be created. Specifying 0 - /// means never, 1.0 means always. The value needs to be >= 0.0 and <= 1.0 The default is null - /// (disabled). + /// A percentage of sessions in which a replay will be created. + /// The value needs to be >= 0.0 and <= 1.0. + /// Specifying 0 means none, 1.0 means 100 %. Defaults to null (disabled). double? get sessionSampleRate => _sessionSampleRate; - - /// Indicates the percentage in which the replay for the session will be created. Specifying 0 - /// means never, 1.0 means always. The value needs to be >= 0.0 and <= 1.0 The default is null - /// (disabled). set sessionSampleRate(double? value) { assert(value == null || (value >= 0 && value <= 1)); _sessionSampleRate = value; @@ -19,14 +15,10 @@ class SentryReplayOptions { double? _errorSampleRate; - /// Indicates the percentage in which a 30 seconds replay will be send with error events. - /// Specifying 0 means never, 1.0 means always. The value needs to be >= 0.0 and <= 1.0. The - /// default is null (disabled). + /// A percentage of errors that will be accompanied by a 30 seconds replay. + /// The value needs to be >= 0.0 and <= 1.0. + /// Specifying 0 means none, 1.0 means 100 %. Defaults to null (disabled). double? get errorSampleRate => _errorSampleRate; - - /// Indicates the percentage in which a 30 seconds replay will be send with error events. - /// Specifying 0 means never, 1.0 means always. The value needs to be >= 0.0 and <= 1.0. The - /// default is null (disabled). set errorSampleRate(double? value) { assert(value == null || (value >= 0 && value <= 1)); _errorSampleRate = value; From 114ed8640026105b725a1007e236ce20ab969dfc Mon Sep 17 00:00:00 2001 From: Ivan Dlugos <6349682+vaind@users.noreply.github.com> Date: Thu, 13 Jun 2024 09:10:51 +0200 Subject: [PATCH 73/74] feat: associate dart errors with replays (#2070) * feat: associate dart errors with replays * ktlint * cleanup * tests --- .../src/protocol/sentry_trace_context.dart | 17 +++++++++++---- dart/lib/src/scope.dart | 11 +++++++++- dart/lib/src/sentry_baggage.dart | 10 +++++++++ dart/lib/src/sentry_client.dart | 10 ++++----- dart/lib/src/sentry_trace_context_header.dart | 13 ++++++++++++ .../protocol/sentry_baggage_header_test.dart | 16 ++++++++++++-- dart/test/scope_test.dart | 19 ++++++++++------- dart/test/sentry_client_test.dart | 9 +++++++- .../sentry_trace_context_header_test.dart | 19 ++++++++++++++--- dart/test/sentry_trace_context_test.dart | 21 ++++++++++++------- .../flutter/SentryFlutterReplayRecorder.kt | 1 + flutter/example/lib/main.dart | 1 + .../replay_event_processor.dart | 8 +++---- .../src/native/java/sentry_native_java.dart | 15 +++++++++++++ 14 files changed, 134 insertions(+), 36 deletions(-) diff --git a/dart/lib/src/protocol/sentry_trace_context.dart b/dart/lib/src/protocol/sentry_trace_context.dart index 25c4ca7ad8..2a9c3bb2fc 100644 --- a/dart/lib/src/protocol/sentry_trace_context.dart +++ b/dart/lib/src/protocol/sentry_trace_context.dart @@ -17,6 +17,9 @@ class SentryTraceContext { /// Id of a parent span final SpanId? parentSpanId; + /// Replay associated with this trace. + final SentryId? replayId; + /// Whether the span is sampled or not final bool? sampled; @@ -45,6 +48,9 @@ class SentryTraceContext { ? null : SpanId.fromId(json['parent_span_id'] as String), traceId: SentryId.fromId(json['trace_id'] as String), + replayId: json['replay_id'] == null + ? null + : SentryId.fromId(json['replay_id'] as String), description: json['description'] as String?, status: json['status'] == null ? null @@ -61,6 +67,7 @@ class SentryTraceContext { 'trace_id': traceId.toString(), 'op': operation, if (parentSpanId != null) 'parent_span_id': parentSpanId!.toString(), + if (replayId != null) 'replay_id': replayId!.toString(), if (description != null) 'description': description, if (status != null) 'status': status!.toString(), if (origin != null) 'origin': origin, @@ -76,6 +83,7 @@ class SentryTraceContext { parentSpanId: parentSpanId, sampled: sampled, origin: origin, + replayId: replayId, ); SentryTraceContext({ @@ -87,6 +95,7 @@ class SentryTraceContext { this.description, this.status, this.origin, + this.replayId, }) : traceId = traceId ?? SentryId.newId(), spanId = spanId ?? SpanId.newId(); @@ -94,9 +103,9 @@ class SentryTraceContext { factory SentryTraceContext.fromPropagationContext( PropagationContext propagationContext) { return SentryTraceContext( - traceId: propagationContext.traceId, - spanId: propagationContext.spanId, - operation: 'default', - ); + traceId: propagationContext.traceId, + spanId: propagationContext.spanId, + operation: 'default', + replayId: propagationContext.baggage?.getReplayId()); } } diff --git a/dart/lib/src/scope.dart b/dart/lib/src/scope.dart index 3fef9a92a2..0b35137ac5 100644 --- a/dart/lib/src/scope.dart +++ b/dart/lib/src/scope.dart @@ -97,6 +97,13 @@ class Scope { /// they must be JSON-serializable. Map get extra => Map.unmodifiable(_extra); + /// Active replay recording. + @internal + SentryId? get replayId => _replayId; + @internal + set replayId(SentryId? value) => _replayId = value; + SentryId? _replayId; + final Contexts _contexts = Contexts(); /// Unmodifiable map of the scope contexts key/value @@ -237,6 +244,7 @@ class Scope { _tags.clear(); _extra.clear(); _eventProcessors.clear(); + _replayId = null; _clearBreadcrumbsSync(); _setUserSync(null); @@ -425,7 +433,8 @@ class Scope { ..fingerprint = List.from(fingerprint) .._transaction = _transaction ..span = span - .._enableScopeSync = false; + .._enableScopeSync = false + .._replayId = _replayId; clone._setUserSync(user); diff --git a/dart/lib/src/sentry_baggage.dart b/dart/lib/src/sentry_baggage.dart index 25aab900f4..3ae6a2a2ac 100644 --- a/dart/lib/src/sentry_baggage.dart +++ b/dart/lib/src/sentry_baggage.dart @@ -109,6 +109,9 @@ class SentryBaggage { if (scope.user?.segment != null) { setUserSegment(scope.user!.segment!); } + if (scope.replayId != null && scope.replayId != SentryId.empty()) { + setReplayId(scope.replayId.toString()); + } } static Map _extractKeyValuesFromBaggageString( @@ -201,5 +204,12 @@ class SentryBaggage { return double.tryParse(sampleRate); } + void setReplayId(String value) => set('sentry-replay_id', value); + + SentryId? getReplayId() { + final replayId = get('sentry-replay_id'); + return replayId == null ? null : SentryId.fromId(replayId); + } + Map get keyValues => Map.unmodifiable(_keyValues); } diff --git a/dart/lib/src/sentry_client.dart b/dart/lib/src/sentry_client.dart index a1f20ded61..ee5419acbb 100644 --- a/dart/lib/src/sentry_client.dart +++ b/dart/lib/src/sentry_client.dart @@ -143,15 +143,15 @@ class SentryClient { var traceContext = scope?.span?.traceContext(); if (traceContext == null) { - if (scope?.propagationContext.baggage == null) { - scope?.propagationContext.baggage = - SentryBaggage({}, logger: _options.logger); - scope?.propagationContext.baggage?.setValuesFromScope(scope, _options); - } if (scope != null) { + scope.propagationContext.baggage ??= + SentryBaggage({}, logger: _options.logger) + ..setValuesFromScope(scope, _options); traceContext = SentryTraceContextHeader.fromBaggage( scope.propagationContext.baggage!); } + } else { + traceContext.replayId = scope?.replayId; } final envelope = SentryEnvelope.fromEvent( diff --git a/dart/lib/src/sentry_trace_context_header.dart b/dart/lib/src/sentry_trace_context_header.dart index bcb1d0b1bb..b178e29d7a 100644 --- a/dart/lib/src/sentry_trace_context_header.dart +++ b/dart/lib/src/sentry_trace_context_header.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import 'protocol/sentry_id.dart'; import 'sentry_baggage.dart'; import 'sentry_options.dart'; @@ -13,6 +15,7 @@ class SentryTraceContextHeader { this.transaction, this.sampleRate, this.sampled, + this.replayId, }); final SentryId traceId; @@ -25,6 +28,9 @@ class SentryTraceContextHeader { final String? sampleRate; final String? sampled; + @internal + SentryId? replayId; + /// Deserializes a [SentryTraceContextHeader] from JSON [Map]. factory SentryTraceContextHeader.fromJson(Map json) { return SentryTraceContextHeader( @@ -37,6 +43,8 @@ class SentryTraceContextHeader { transaction: json['transaction'], sampleRate: json['sample_rate'], sampled: json['sampled'], + replayId: + json['replay_id'] == null ? null : SentryId.fromId(json['replay_id']), ); } @@ -52,6 +60,7 @@ class SentryTraceContextHeader { if (transaction != null) 'transaction': transaction, if (sampleRate != null) 'sample_rate': sampleRate, if (sampled != null) 'sampled': sampled, + if (replayId != null) 'replay_id': replayId.toString(), }; } @@ -83,6 +92,9 @@ class SentryTraceContextHeader { if (sampled != null) { baggage.setSampled(sampled!); } + if (replayId != null) { + baggage.setReplayId(replayId.toString()); + } return baggage; } @@ -92,6 +104,7 @@ class SentryTraceContextHeader { baggage.get('sentry-public_key').toString(), release: baggage.get('sentry-release'), environment: baggage.get('sentry-environment'), + replayId: baggage.getReplayId(), ); } } diff --git a/dart/test/protocol/sentry_baggage_header_test.dart b/dart/test/protocol/sentry_baggage_header_test.dart index 38428be41a..cb4f0be6bf 100644 --- a/dart/test/protocol/sentry_baggage_header_test.dart +++ b/dart/test/protocol/sentry_baggage_header_test.dart @@ -21,11 +21,23 @@ void main() { baggage.setTransaction('transaction'); baggage.setSampleRate('1.0'); baggage.setSampled('false'); + final replayId = SentryId.newId().toString(); + baggage.setReplayId(replayId); final baggageHeader = SentryBaggageHeader.fromBaggage(baggage); - expect(baggageHeader.value, - 'sentry-trace_id=$id,sentry-public_key=publicKey,sentry-release=release,sentry-environment=environment,sentry-user_id=userId,sentry-user_segment=userSegment,sentry-transaction=transaction,sentry-sample_rate=1.0,sentry-sampled=false'); + expect( + baggageHeader.value, + 'sentry-trace_id=$id,' + 'sentry-public_key=publicKey,' + 'sentry-release=release,' + 'sentry-environment=environment,' + 'sentry-user_id=userId,' + 'sentry-user_segment=userSegment,' + 'sentry-transaction=transaction,' + 'sentry-sample_rate=1.0,' + 'sentry-sampled=false,' + 'sentry-replay_id=$replayId'); }); }); } diff --git a/dart/test/scope_test.dart b/dart/test/scope_test.dart index 66cc543b6b..6593a58638 100644 --- a/dart/test/scope_test.dart +++ b/dart/test/scope_test.dart @@ -86,6 +86,14 @@ void main() { expect(sut.fingerprint, fingerprints); }); + test('sets replay ID', () { + final sut = fixture.getSut(); + + sut.replayId = SentryId.fromId('1'); + + expect(sut.replayId, SentryId.fromId('1')); + }); + test('adds $Breadcrumb', () { final sut = fixture.getSut(); @@ -305,6 +313,7 @@ void main() { sut.level = SentryLevel.debug; sut.transaction = 'test'; sut.span = null; + sut.replayId = SentryId.newId(); final user = SentryUser(id: 'test'); sut.setUser(user); @@ -320,21 +329,15 @@ void main() { sut.clear(); expect(sut.breadcrumbs.length, 0); - expect(sut.level, null); - expect(sut.transaction, null); expect(sut.span, null); - expect(sut.user, null); - expect(sut.fingerprint.length, 0); - expect(sut.tags.length, 0); - expect(sut.extra.length, 0); - expect(sut.eventProcessors.length, 0); + expect(sut.replayId, isNull); }); test('clones', () async { @@ -347,6 +350,7 @@ void main() { sut.addAttachment(SentryAttachment.fromIntList([0, 0, 0, 0], 'test.txt')); sut.span = NoOpSentrySpan(); sut.level = SentryLevel.warning; + sut.replayId = SentryId.newId(); await sut.setUser(SentryUser(id: 'id')); await sut.setTag('key', 'vakye'); await sut.setExtra('key', 'vakye'); @@ -367,6 +371,7 @@ void main() { true, ); expect(sut.span, clone.span); + expect(sut.replayId, clone.replayId); }); test('clone does not additionally call observers', () async { diff --git a/dart/test/sentry_client_test.dart b/dart/test/sentry_client_test.dart index b10ae154f7..fc082a6506 100644 --- a/dart/test/sentry_client_test.dart +++ b/dart/test/sentry_client_test.dart @@ -809,7 +809,8 @@ void main() { ..fingerprint = fingerprint ..addBreadcrumb(crumb) ..setTag(scopeTagKey, scopeTagValue) - ..setExtra(scopeExtraKey, scopeExtraValue); + ..setExtra(scopeExtraKey, scopeExtraValue) + ..replayId = SentryId.fromId('1'); scope.setUser(user); }); @@ -835,6 +836,8 @@ void main() { scopeExtraKey: scopeExtraValue, eventExtraKey: eventExtraValue, }); + expect( + capturedEnvelope.header.traceContext?.replayId, SentryId.fromId('1')); }); }); @@ -1321,6 +1324,7 @@ void main() { final client = fixture.getSut(); final scope = Scope(fixture.options); + scope.replayId = SentryId.newId(); scope.span = SentrySpan(fixture.tracer, fixture.tracer.context, MockHub()); @@ -1328,6 +1332,7 @@ void main() { final envelope = fixture.transport.envelopes.first; expect(envelope.header.traceContext, isNotNull); + expect(envelope.header.traceContext?.replayId, scope.replayId); }); test('captureEvent adds attachments from hint', () async { @@ -1384,12 +1389,14 @@ void main() { final context = SentryTraceContextHeader.fromJson({ 'trace_id': '${tr.eventId}', 'public_key': '123', + 'replay_id': '456', }); await client.captureTransaction(tr, traceContext: context); final envelope = fixture.transport.envelopes.first; expect(envelope.header.traceContext, isNotNull); + expect(envelope.header.traceContext?.replayId, SentryId.fromId('456')); }); test('captureUserFeedback calls flush', () async { diff --git a/dart/test/sentry_trace_context_header_test.dart b/dart/test/sentry_trace_context_header_test.dart index 6ba6d93bc2..6da11b069a 100644 --- a/dart/test/sentry_trace_context_header_test.dart +++ b/dart/test/sentry_trace_context_header_test.dart @@ -14,7 +14,8 @@ void main() { 'user_segment': 'user_segment', 'transaction': 'transaction', 'sample_rate': '1.0', - 'sampled': 'false' + 'sampled': 'false', + 'replay_id': '456', }; final context = SentryTraceContextHeader.fromJson(mapJson); @@ -28,6 +29,7 @@ void main() { expect(context.transaction, 'transaction'); expect(context.sampleRate, '1.0'); expect(context.sampled, 'false'); + expect(context.replayId, SentryId.fromId('456')); }); test('toJson', () { @@ -39,8 +41,19 @@ void main() { test('to baggage', () { final baggage = context.toBaggage(); - expect(baggage.toHeaderString(), - 'sentry-trace_id=${id.toString()},sentry-public_key=123,sentry-release=release,sentry-environment=environment,sentry-user_id=user_id,sentry-user_segment=user_segment,sentry-transaction=transaction,sentry-sample_rate=1.0,sentry-sampled=false'); + expect( + baggage.toHeaderString(), + 'sentry-trace_id=${id.toString()},' + 'sentry-public_key=123,' + 'sentry-release=release,' + 'sentry-environment=environment,' + 'sentry-user_id=user_id,' + 'sentry-user_segment=user_segment,' + 'sentry-transaction=transaction,' + 'sentry-sample_rate=1.0,' + 'sentry-sampled=false,' + 'sentry-replay_id=456', + ); }); }); } diff --git a/dart/test/sentry_trace_context_test.dart b/dart/test/sentry_trace_context_test.dart index dde599bef1..13dbe1fd62 100644 --- a/dart/test/sentry_trace_context_test.dart +++ b/dart/test/sentry_trace_context_test.dart @@ -16,27 +16,31 @@ void main() { expect(map['description'], 'desc'); expect(map['status'], 'aborted'); expect(map['origin'], 'auto.ui'); + expect(map['replay_id'], isNotNull); }); test('fromJson deserializes', () { final map = { 'op': 'op', - 'span_id': '0000000000000000', - 'trace_id': '00000000000000000000000000000000', - 'parent_span_id': '0000000000000000', + 'span_id': '0000000000000001', + 'trace_id': '00000000000000000000000000000002', + 'parent_span_id': '0000000000000003', 'description': 'desc', 'status': 'aborted', - 'origin': 'auto.ui' + 'origin': 'auto.ui', + 'replay_id': '00000000000000000000000000000004' }; final traceContext = SentryTraceContext.fromJson(map); expect(traceContext.description, 'desc'); expect(traceContext.operation, 'op'); - expect(traceContext.spanId.toString(), '0000000000000000'); - expect(traceContext.traceId.toString(), '00000000000000000000000000000000'); - expect(traceContext.parentSpanId.toString(), '0000000000000000'); + expect(traceContext.spanId.toString(), '0000000000000001'); + expect(traceContext.traceId.toString(), '00000000000000000000000000000002'); + expect(traceContext.parentSpanId.toString(), '0000000000000003'); expect(traceContext.status.toString(), 'aborted'); expect(traceContext.sampled, true); + expect( + traceContext.replayId.toString(), '00000000000000000000000000000004'); }); } @@ -48,6 +52,7 @@ class Fixture { description: 'desc', sampled: true, status: SpanStatus.aborted(), - origin: 'auto.ui'); + origin: 'auto.ui', + replayId: SentryId.newId()); } } diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt index 3d57b12d77..41209f75b6 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt @@ -27,6 +27,7 @@ internal class SentryFlutterReplayRecorder( "width" to config.recordingWidth, "height" to config.recordingHeight, "frameRate" to config.frameRate, + "replayId" to integration.getReplayId().toString(), ), ) } catch (ignored: Exception) { diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index 0fa822ff54..7744a884e7 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -91,6 +91,7 @@ Future setupSentry( options.navigatorKey = navigatorKey; options.experimental.replay.sessionSampleRate = 1.0; + options.experimental.replay.errorSampleRate = 1.0; _isIntegrationTest = isIntegrationTest; if (_isIntegrationTest) { diff --git a/flutter/lib/src/event_processor/replay_event_processor.dart b/flutter/lib/src/event_processor/replay_event_processor.dart index 65ed67141c..4be68a4d00 100644 --- a/flutter/lib/src/event_processor/replay_event_processor.dart +++ b/flutter/lib/src/event_processor/replay_event_processor.dart @@ -13,11 +13,9 @@ class ReplayEventProcessor implements EventProcessor { Future apply(SentryEvent event, Hint hint) async { if (event.eventId != SentryId.empty() && event.exceptions?.isNotEmpty == true) { - final isCrash = event.exceptions! - .any((element) => element.mechanism?.handled == false); - // ignore: unused_local_variable - final replayId = - await _binding.sendReplayForEvent(event.eventId, isCrash); + final isCrash = + event.exceptions!.any((e) => e.mechanism?.handled == false); + await _binding.sendReplayForEvent(event.eventId, isCrash); } return event; } diff --git a/flutter/lib/src/native/java/sentry_native_java.dart b/flutter/lib/src/native/java/sentry_native_java.dart index 241957e5d7..5cdcbc3ed3 100644 --- a/flutter/lib/src/native/java/sentry_native_java.dart +++ b/flutter/lib/src/native/java/sentry_native_java.dart @@ -33,6 +33,9 @@ class SentryNativeJava extends SentryNativeChannel { channel.setMethodCallHandler((call) async { switch (call.method) { case 'ReplayRecorder.start': + final replayId = + SentryId.fromId(call.arguments['replayId'] as String); + _startRecorder( call.arguments['directory'] as String, ScreenshotRecorderConfig( @@ -41,10 +44,22 @@ class SentryNativeJava extends SentryNativeChannel { frameRate: call.arguments['frameRate'] as int, ), ); + + Sentry.configureScope((s) { + // ignore: invalid_use_of_internal_member + s.replayId = replayId; + }); + break; case 'ReplayRecorder.stop': await _replayRecorder?.stop(); _replayRecorder = null; + + Sentry.configureScope((s) { + // ignore: invalid_use_of_internal_member + s.replayId = null; + }); + break; case 'ReplayRecorder.pause': await _replayRecorder?.stop(); From c26a8a2bb02cddcd8bdc12055c5e24391a27739b Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 27 Jun 2024 18:53:50 +0200 Subject: [PATCH 74/74] chote: remove path dependency --- flutter/lib/src/native/java/sentry_native_java.dart | 3 +-- flutter/pubspec.yaml | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/flutter/lib/src/native/java/sentry_native_java.dart b/flutter/lib/src/native/java/sentry_native_java.dart index aad1e6b90d..0ce7117afd 100644 --- a/flutter/lib/src/native/java/sentry_native_java.dart +++ b/flutter/lib/src/native/java/sentry_native_java.dart @@ -2,7 +2,6 @@ import 'dart:io'; import 'dart:ui'; import 'package:meta/meta.dart'; -import 'package:path/path.dart' as path; import '../../../sentry_flutter.dart'; import '../../event_processor/replay_event_processor.dart'; @@ -88,7 +87,7 @@ class SentryNativeJava extends SentryNativeChannel { var imageData = await image.toByteData(format: ImageByteFormat.png); if (imageData != null) { final timestamp = DateTime.now().millisecondsSinceEpoch; - final filePath = path.join(cacheDir, "$timestamp.png"); + final filePath = "$cacheDir/$timestamp.png"; _options.logger( SentryLevel.debug, diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 3c6be0180e..d2a244f171 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -27,7 +27,6 @@ dependencies: package_info_plus: '>=1.0.0' meta: ^1.3.0 ffi: ^2.0.0 - path: ^1.8.0 dev_dependencies: build_runner: ^2.4.2