Skip to content

Commit

Permalink
Add textScale(r) value to Flutter context (#1886)
Browse files Browse the repository at this point in the history
* entrich event contexts app with text scale

* read textScale from sentry widget event processor

* revert change

* revert change

* update changelog

* update changelog

* Use scale method instead of deprecated `textScaleFactor`

* fix changelog

* fix changelog

* use maybeTextScaleFactorOf method

* fix changelog

* fix changelog

* use textScaleFactorOf

* fix tests

* update doc

* Update CHANGELOG.md

---------

Co-authored-by: Giancarlo Buenaflor <giancarlo_buenaflor@yahoo.com>
  • Loading branch information
denrase and buenaflor authored Apr 10, 2024
1 parent be8cafe commit 6034b0a
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 5 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Features

- Add textScale(r) value to Flutter context ([#1886](https://github.com/getsentry/sentry-dart/pull/1886))

### Dependencies

- Bump Android SDK from v7.6.0 to v7.8.0 ([#1977](https://github.com/getsentry/sentry-dart/pull/1977))
Expand Down
9 changes: 9 additions & 0 deletions dart/lib/src/protocol/sentry_app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class SentryApp {
this.appMemory,
this.inForeground,
this.viewNames,
this.textScale,
});

/// Human readable application name, as it appears on the platform.
Expand Down Expand Up @@ -52,6 +53,9 @@ class SentryApp {
/// The names of the currently visible views.
final List<String>? viewNames;

/// The current text scale. Only available on Flutter.
final double? textScale;

/// Deserializes a [SentryApp] from JSON [Map].
factory SentryApp.fromJson(Map<String, dynamic> data) {
final viewNamesJson = data['view_names'] as List<dynamic>?;
Expand All @@ -68,6 +72,7 @@ class SentryApp {
appMemory: data['app_memory'],
inForeground: data['in_foreground'],
viewNames: viewNamesJson?.map((e) => e as String).toList(),
textScale: data['text_scale'],
);
}

Expand All @@ -84,6 +89,7 @@ class SentryApp {
if (appMemory != null) 'app_memory': appMemory!,
if (inForeground != null) 'in_foreground': inForeground!,
if (viewNames != null && viewNames!.isNotEmpty) 'view_names': viewNames!,
if (textScale != null) 'text_scale': textScale!,
};
}

Expand All @@ -98,6 +104,7 @@ class SentryApp {
appMemory: appMemory,
inForeground: inForeground,
viewNames: viewNames,
textScale: textScale,
);

SentryApp copyWith({
Expand All @@ -111,6 +118,7 @@ class SentryApp {
int? appMemory,
bool? inForeground,
List<String>? viewNames,
double? textScale,
}) =>
SentryApp(
name: name ?? this.name,
Expand All @@ -123,5 +131,6 @@ class SentryApp {
appMemory: appMemory ?? this.appMemory,
inForeground: inForeground ?? this.inForeground,
viewNames: viewNames ?? this.viewNames,
textScale: textScale ?? this.textScale,
);
}
6 changes: 6 additions & 0 deletions dart/test/protocol/sentry_app_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ void main() {
deviceAppHash: 'fixture-deviceAppHash',
inForeground: true,
viewNames: ['fixture-viewName', 'fixture-viewName2'],
textScale: 2.0,
);

final sentryAppJson = <String, dynamic>{
Expand All @@ -27,6 +28,7 @@ void main() {
'device_app_hash': 'fixture-deviceAppHash',
'in_foreground': true,
'view_names': ['fixture-viewName', 'fixture-viewName2'],
'text_scale': 2.0,
};

group('json', () {
Expand All @@ -42,6 +44,7 @@ void main() {
expect(json['device_app_hash'], 'fixture-deviceAppHash');
expect(json['in_foreground'], true);
expect(json['view_names'], ['fixture-viewName', 'fixture-viewName2']);
expect(json['text_scale'], 2.0);
});
test('fromJson', () {
final sentryApp = SentryApp.fromJson(sentryAppJson);
Expand All @@ -56,6 +59,7 @@ void main() {
expect(json['device_app_hash'], 'fixture-deviceAppHash');
expect(json['in_foreground'], true);
expect(json['view_names'], ['fixture-viewName', 'fixture-viewName2']);
expect(json['text_scale'], 2.0);
});
});

Expand Down Expand Up @@ -86,6 +90,7 @@ void main() {
deviceAppHash: 'hash1',
inForeground: true,
viewNames: ['screen1'],
textScale: 3.0,
);

expect('name1', copy.name);
Expand All @@ -97,6 +102,7 @@ void main() {
expect('hash1', copy.deviceAppHash);
expect(true, copy.inForeground);
expect(['screen1'], copy.viewNames);
expect(3.0, copy.textScale);
});
});
}
1 change: 1 addition & 0 deletions flutter/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ Future<void> setupSentry(

options.maxRequestBodySize = MaxRequestBodySize.always;
options.maxResponseBodySize = MaxResponseBodySize.always;
options.navigatorKey = navigatorKey;

_isIntegrationTest = isIntegrationTest;
if (_isIntegrationTest) {
Expand Down
31 changes: 31 additions & 0 deletions flutter/lib/src/event_processor/widget_event_processor.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import 'dart:async';

import 'package:flutter/widgets.dart';

import '../../sentry_flutter.dart';

class WidgetEventProcessor implements EventProcessor {
@override
FutureOr<SentryEvent?> apply(SentryEvent event, {Hint? hint}) {
if (event is SentryTransaction) {
return event;
}
if (event.exceptions == null && event.throwable == null) {
return event;
}
final context = sentryWidgetGlobalKey.currentContext;
if (context == null) {
return event;
}

// ignore: deprecated_member_use
final textScale = MediaQuery.textScaleFactorOf(context);
return event.copyWith(
contexts: event.contexts.copyWith(
app: event.contexts.app?.copyWith(
textScale: textScale,
),
),
);
}
}
10 changes: 6 additions & 4 deletions flutter/lib/src/sentry_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import '../sentry_flutter.dart';
import 'event_processor/android_platform_exception_event_processor.dart';
import 'event_processor/flutter_exception_event_processor.dart';
import 'event_processor/platform_exception_event_processor.dart';
import 'event_processor/widget_event_processor.dart';
import 'frame_callback_handler.dart';
import 'integrations/connectivity/connectivity_integration.dart';
import 'integrations/screenshot_integration.dart';
Expand Down Expand Up @@ -110,12 +111,13 @@ mixin SentryFlutter {
options.addScopeObserver(NativeScopeObserver(_native!));
}

var flutterEventProcessor = FlutterEnricherEventProcessor(options);
options.addEventProcessor(flutterEventProcessor);
options.addEventProcessor(FlutterEnricherEventProcessor(options));
options.addEventProcessor(WidgetEventProcessor());

if (options.platformChecker.platform.isAndroid) {
options
.addEventProcessor(AndroidPlatformExceptionEventProcessor(options));
options.addEventProcessor(
AndroidPlatformExceptionEventProcessor(options),
);
}

options.addEventProcessor(PlatformExceptionEventProcessor());
Expand Down
10 changes: 9 additions & 1 deletion flutter/lib/src/sentry_widget.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import 'package:flutter/cupertino.dart';
import 'package:meta/meta.dart';
import '../sentry_flutter.dart';

/// Key which is used to identify the [SentryWidget]
@internal
final sentryWidgetGlobalKey = GlobalKey(debugLabel: 'sentry_widget');

/// This widget serves as a wrapper to include Sentry widgets such
/// as [SentryScreenshotWidget] and [SentryUserInteractionWidget].
class SentryWidget extends StatefulWidget {
Expand All @@ -18,6 +23,9 @@ class _SentryWidgetState extends State<SentryWidget> {
Widget content = widget.child;
content = SentryScreenshotWidget(child: content);
content = SentryUserInteractionWidget(child: content);
return content;
return Container(
key: sentryWidgetGlobalKey,
child: content,
);
}
}
44 changes: 44 additions & 0 deletions flutter/test/event_processor/widget_event_processor_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:sentry_flutter/src/event_processor/widget_event_processor.dart';

void main() {
TestWidgetsFlutterBinding.ensureInitialized();
late Fixture fixture;

setUp(() {
fixture = Fixture();
});

testWidgets('adds screenshot attachment dart:io', (tester) async {
await tester.runAsync(() async {
final sut = fixture.getSut();
await tester.pumpWidget(
SentryWidget(
child: Text(
'Catching Pokémon is a snap!',
textDirection: TextDirection.ltr,
),
),
);

final throwable = Exception();
SentryEvent? event = SentryEvent(throwable: throwable);
event = event.copyWith(
contexts: event.contexts.copyWith(
app: SentryApp(),
),
);
event = await sut.apply(event);

expect(event?.contexts.app?.textScale, 1.0);
});
});
}

class Fixture {
WidgetEventProcessor getSut() {
return WidgetEventProcessor();
}
}

0 comments on commit 6034b0a

Please sign in to comment.