Skip to content

Commit 0fb45d0

Browse files
markushiweb-flowbuenaflor
authored
Prepare SDK for upcoming Session Replay API changes on Android (#2977)
* Prepare SDK for upcoming Android changes * Fix code formatting * chore: update flutter/scripts/update-android.sh to 8.14.0 * chore: update flutter/scripts/update-android.sh to 8.16.0 * Update CHANGELOG.md * Update CHANGELOG.md * Update build.gradle * Update test * Update CHANGELOG * Fix detekt * Update CHANGELOG * Update * Ensure async consistency * Update * Fix analyze --------- Co-authored-by: GitHub <[email protected]> Co-authored-by: Giancarlo Buenaflor <[email protected]> Co-authored-by: Giancarlo Buenaflor <[email protected]>
1 parent 4481076 commit 0fb45d0

21 files changed

+211
-163
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### Dependencies
6+
7+
- Bump Android SDK from v8.13.2 to v8.17.0 ([#2977](https://github.com/getsentry/sentry-dart/pull/2977))
8+
- [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#8170)
9+
- [diff](https://github.com/getsentry/sentry-java/compare/8.13.2...8.17.0)
10+
311
## 9.5.0
412

513
### Features

flutter/android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ android {
6060
}
6161

6262
dependencies {
63-
api 'io.sentry:sentry-android:8.13.2'
63+
api 'io.sentry:sentry-android:8.17.0'
6464
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
6565

6666
// Required -- JUnit 4 framework

flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutter.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,10 @@ class SentryFlutter {
201201
replayOptions.sessionSampleRate = (data["sessionSampleRate"] as? Number)?.toDouble()
202202
replayOptions.onErrorSampleRate = (data["onErrorSampleRate"] as? Number)?.toDouble()
203203

204-
// Disable native tracking of orientation change (causes replay restart)
204+
// Disable native tracking of window sizes
205205
// because we don't have the new size from Flutter yet. Instead, we'll
206206
// trigger onConfigurationChanged() manually in setReplayConfig().
207-
replayOptions.setTrackOrientationChange(false)
207+
replayOptions.isTrackConfiguration = false
208208

209209
@Suppress("UNCHECKED_CAST")
210210
val tags = (data["tags"] as? Map<String, Any>) ?: mapOf()

flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt

Lines changed: 26 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,9 @@ package io.sentry.flutter
33
import android.annotation.SuppressLint
44
import android.app.Activity
55
import android.content.Context
6-
import android.content.res.Configuration
7-
import android.graphics.Point
8-
import android.graphics.Rect
96
import android.os.Build
10-
import android.os.Build.VERSION
11-
import android.os.Build.VERSION_CODES
127
import android.os.Looper
138
import android.util.Log
14-
import android.view.WindowManager
159
import io.flutter.embedding.engine.plugins.FlutterPlugin
1610
import io.flutter.embedding.engine.plugins.activity.ActivityAware
1711
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
@@ -49,18 +43,6 @@ class SentryFlutterPlugin :
4943
private lateinit var context: Context
5044
private lateinit var sentryFlutter: SentryFlutter
5145

52-
// Note: initial config because we don't yet have the numbers of the actual Flutter widget.
53-
// See how SentryFlutterReplayRecorder.start() handles it. New settings will be set by setReplayConfig() method below.
54-
private var replayConfig =
55-
ScreenshotRecorderConfig(
56-
recordingWidth = VIDEO_BLOCK_SIZE,
57-
recordingHeight = VIDEO_BLOCK_SIZE,
58-
scaleFactorX = 1.0f,
59-
scaleFactorY = 1.0f,
60-
frameRate = 1,
61-
bitRate = 75000,
62-
)
63-
6446
private var activity: WeakReference<Activity>? = null
6547
private var pluginRegistrationTime: Long? = null
6648

@@ -160,18 +142,6 @@ class SentryFlutterPlugin :
160142
context.applicationContext,
161143
dateProvider = CurrentDateProvider.getInstance(),
162144
recorderProvider = { SentryFlutterReplayRecorder(channel, replay!!) },
163-
recorderConfigProvider = {
164-
Log.i(
165-
"Sentry",
166-
"Replay configuration requested. Returning: %dx%d at %d FPS, %d BPS".format(
167-
replayConfig.recordingWidth,
168-
replayConfig.recordingHeight,
169-
replayConfig.frameRate,
170-
replayConfig.bitRate,
171-
),
172-
)
173-
replayConfig
174-
},
175145
replayCacheProvider = null,
176146
)
177147
replay!!.breadcrumbConverter = SentryFlutterReplayBreadcrumbConverter()
@@ -473,7 +443,8 @@ class SentryFlutterPlugin :
473443

474444
private const val NATIVE_CRASH_WAIT_TIME = 500L
475445

476-
@JvmStatic fun privateSentryGetReplayIntegration(): ReplayIntegration? = replay
446+
@JvmStatic
447+
fun privateSentryGetReplayIntegration(): ReplayIntegration? = replay
477448

478449
private fun crash() {
479450
val exception = RuntimeException("FlutterSentry Native Integration: Sample RuntimeException")
@@ -515,8 +486,28 @@ class SentryFlutterPlugin :
515486
// Since codec block size is 16, so we have to adjust the width and height to it,
516487
// otherwise the codec might fail to configure on some devices, see
517488
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/media/java/android/media/MediaCodecInfo.java;l=1999-2001
489+
val windowWidth = call.argument("windowWidth") as? Double ?: 0.0
490+
val windowHeight = call.argument("windowHeight") as? Double ?: 0.0
491+
518492
var width = call.argument("width") as? Double ?: 0.0
519493
var height = call.argument("height") as? Double ?: 0.0
494+
495+
val invalidConfig =
496+
width == 0.0 ||
497+
height == 0.0 ||
498+
windowWidth == 0.0 ||
499+
windowHeight == 0.0
500+
501+
if (invalidConfig) {
502+
result.error(
503+
"5",
504+
"Replay config is not valid: width: $width, height: $height, " +
505+
"windowWidth: $windowWidth, windowHeight: $windowHeight",
506+
null,
507+
)
508+
return
509+
}
510+
520511
// First update the smaller dimension, as changing that will affect the screen ratio more.
521512
if (width < height) {
522513
val newWidth = width.adjustReplaySizeToBlockSize()
@@ -528,23 +519,12 @@ class SentryFlutterPlugin :
528519
height = newHeight
529520
}
530521

531-
val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
532-
val screenBounds =
533-
if (VERSION.SDK_INT >= VERSION_CODES.R) {
534-
wm.currentWindowMetrics.bounds
535-
} else {
536-
val screenBounds = Point()
537-
@Suppress("DEPRECATION")
538-
wm.defaultDisplay.getRealSize(screenBounds)
539-
Rect(0, 0, screenBounds.x, screenBounds.y)
540-
}
541-
542-
replayConfig =
522+
val replayConfig =
543523
ScreenshotRecorderConfig(
544524
recordingWidth = width.roundToInt(),
545525
recordingHeight = height.roundToInt(),
546-
scaleFactorX = width.toFloat() / screenBounds.width().toFloat(),
547-
scaleFactorY = height.toFloat() / screenBounds.height().toFloat(),
526+
scaleFactorX = width.toFloat() / windowWidth.toFloat(),
527+
scaleFactorY = height.toFloat() / windowHeight.toFloat(),
548528
frameRate = call.argument("frameRate") as? Int ?: 0,
549529
bitRate = call.argument("bitRate") as? Int ?: 0,
550530
)
@@ -557,7 +537,7 @@ class SentryFlutterPlugin :
557537
replayConfig.bitRate,
558538
),
559539
)
560-
replay!!.onConfigurationChanged(Configuration())
540+
replay?.onConfigurationChanged(replayConfig)
561541
result.success("")
562542
}
563543

flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterReplayRecorder.kt

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,12 @@ internal class SentryFlutterReplayRecorder(
1212
private val channel: MethodChannel,
1313
private val integration: ReplayIntegration,
1414
) : Recorder {
15-
override fun start(recorderConfig: ScreenshotRecorderConfig) {
16-
// Ignore if this is the initial call before we actually got the configuration from Flutter.
17-
// We'll get another call here when the configuration is changed according to the widget size.
18-
if (recorderConfig.recordingHeight <= VIDEO_BLOCK_SIZE && recorderConfig.recordingWidth <= VIDEO_BLOCK_SIZE) {
19-
return
20-
}
21-
15+
override fun start() {
2216
Handler(Looper.getMainLooper()).post {
2317
try {
2418
channel.invokeMethod(
2519
"ReplayRecorder.start",
2620
mapOf(
27-
"width" to recorderConfig.recordingWidth,
28-
"height" to recorderConfig.recordingHeight,
29-
"frameRate" to recorderConfig.frameRate,
3021
"replayId" to integration.getReplayId().toString(),
3122
),
3223
)
@@ -46,6 +37,33 @@ internal class SentryFlutterReplayRecorder(
4637
}
4738
}
4839

40+
override fun onConfigurationChanged(config: ScreenshotRecorderConfig) {
41+
Handler(Looper.getMainLooper()).post {
42+
try {
43+
channel.invokeMethod(
44+
"ReplayRecorder.onConfigurationChanged",
45+
mapOf(
46+
"width" to config.recordingWidth,
47+
"height" to config.recordingHeight,
48+
"frameRate" to config.frameRate,
49+
),
50+
)
51+
} catch (ignored: Exception) {
52+
Log.w("Sentry", "Failed to propagate configuration change to Flutter", ignored)
53+
}
54+
}
55+
}
56+
57+
override fun reset() {
58+
Handler(Looper.getMainLooper()).post {
59+
try {
60+
channel.invokeMethod("ReplayRecorder.reset", null)
61+
} catch (ignored: Exception) {
62+
Log.w("Sentry", "Failed to reset replay recorder", ignored)
63+
}
64+
}
65+
}
66+
4967
override fun pause() {
5068
Handler(Looper.getMainLooper()).post {
5169
try {

flutter/lib/src/event_processor/screenshot_event_processor.dart

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,11 @@ class ScreenshotEventProcessor implements EventProcessor {
1717

1818
ScreenshotEventProcessor(this._options) {
1919
final targetResolution = _options.screenshotQuality.targetResolution();
20-
_recorder = ScreenshotRecorder(
21-
ScreenshotRecorderConfig(
22-
width: targetResolution,
23-
height: targetResolution,
24-
),
25-
_options,
26-
);
20+
_recorder = ScreenshotRecorder(_options,
21+
config: ScreenshotRecorderConfig(
22+
width: targetResolution,
23+
height: targetResolution,
24+
));
2725
_debouncer = Debouncer(
2826
// ignore: invalid_use_of_internal_member
2927
_options.clock,

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ class CocoaReplayRecorder {
1515

1616
CocoaReplayRecorder(this._options)
1717
: _recorder = ReplayScreenshotRecorder(
18-
ScreenshotRecorderConfig(
19-
pixelRatio: _options.replay.quality.resolutionScalingFactor,
20-
),
2118
_options,
22-
);
19+
) {
20+
_recorder.config = ScreenshotRecorderConfig(
21+
pixelRatio: _options.replay.quality.resolutionScalingFactor,
22+
);
23+
}
2324

2425
Future<Map<String, int>?> captureScreenshot() async {
2526
return _recorder.capture((screenshot) async {

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import 'package:meta/meta.dart';
77

88
import '../../../sentry_flutter.dart';
99
import '../../replay/scheduled_recorder.dart';
10-
import '../../replay/scheduled_recorder_config.dart';
1110
import '../../screenshot/screenshot.dart';
1211
import 'binding.dart' as native;
1312

@@ -18,18 +17,17 @@ class AndroidReplayRecorder extends ScheduledScreenshotRecorder {
1817
_AndroidNativeReplayWorker? _worker;
1918

2019
@internal // visible for testing, used by SentryNativeJava
21-
static AndroidReplayRecorder Function(
22-
ScheduledScreenshotRecorderConfig, SentryFlutterOptions) factory =
20+
static AndroidReplayRecorder Function(SentryFlutterOptions) factory =
2321
AndroidReplayRecorder.new;
2422

25-
AndroidReplayRecorder(super.config, super.options) {
23+
AndroidReplayRecorder(super.options) {
2624
super.callback = _addReplayScreenshot;
2725
}
2826

2927
@override
3028
Future<void> start() async {
3129
final spawningWorker = _AndroidNativeReplayWorker.spawn();
32-
super.start();
30+
await super.start();
3331
_worker = await spawningWorker;
3432
}
3533

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,20 @@ class SentryNativeJava extends SentryNativeChannel {
2424
final replayId =
2525
SentryId.fromId(call.arguments['replayId'] as String);
2626

27-
final config = ScheduledScreenshotRecorderConfig(
28-
width: (call.arguments['width'] as num).toDouble(),
29-
height: (call.arguments['height'] as num).toDouble(),
30-
frameRate: call.arguments['frameRate'] as int);
31-
32-
_replayRecorder = AndroidReplayRecorder.factory(config, options);
27+
_replayRecorder = AndroidReplayRecorder.factory(options);
3328
await _replayRecorder!.start();
34-
3529
hub.configureScope((s) {
3630
// ignore: invalid_use_of_internal_member
3731
s.replayId = replayId;
3832
});
33+
break;
34+
case 'ReplayRecorder.onConfigurationChanged':
35+
final config = ScheduledScreenshotRecorderConfig(
36+
width: (call.arguments['width'] as num).toDouble(),
37+
height: (call.arguments['height'] as num).toDouble(),
38+
frameRate: call.arguments['frameRate'] as int);
3939

40+
await _replayRecorder?.onConfigurationChanged(config);
4041
break;
4142
case 'ReplayRecorder.stop':
4243
hub.configureScope((s) {
@@ -55,6 +56,9 @@ class SentryNativeJava extends SentryNativeChannel {
5556
case 'ReplayRecorder.resume':
5657
await _replayRecorder?.resume();
5758
break;
59+
case 'ReplayRecorder.reset':
60+
// ignored
61+
break;
5862
default:
5963
throw UnimplementedError('Method ${call.method} not implemented');
6064
}

flutter/lib/src/native/sentry_native_channel.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ class SentryNativeChannel
252252
@override
253253
FutureOr<void> setReplayConfig(ReplayConfig config) =>
254254
channel.invokeMethod('setReplayConfig', {
255+
'windowWidth': config.windowWidth,
256+
'windowHeight': config.windowHeight,
255257
'width': config.width,
256258
'height': config.height,
257259
'frameRate': config.frameRate,

0 commit comments

Comments
 (0)