Skip to content

Commit 85c56e2

Browse files
Custom SessionReplay masks in MAUI Android apps (#4121)
1 parent 16fa8dc commit 85c56e2

18 files changed

+320
-7
lines changed

CHANGELOG.md

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

3+
## Unreleased
4+
5+
### Features
6+
7+
- Custom SessionReplay masks in MAUI Android apps ([#4121](https://github.com/getsentry/sentry-dotnet/pull/4121))
8+
39
## 5.6.0
410

511
### Features

samples/Sentry.Samples.Maui/MainPage.xaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?xml version="1.0" encoding="utf-8" ?>
22
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
33
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
4+
xmlns:sentry="http://schemas.sentry.io/maui"
45
x:Class="Sentry.Samples.Maui.MainPage">
56

67
<ScrollView>
@@ -11,6 +12,7 @@
1112

1213
<Image
1314
Source="dotnet_bot.png"
15+
sentry:SessionReplay.Mask="Unmask"
1416
SemanticProperties.Description="Cute dot net bot waving hi to you!"
1517
HeightRequest="200"
1618
HorizontalOptions="Center" />

samples/Sentry.Samples.Maui/MauiProgram.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,17 @@ public static MauiApp CreateMauiApp()
2525
options.Debug = true;
2626
options.SampleRate = 1.0F;
2727

28-
#if ANDROID
28+
#if __ANDROID__
2929
// Currently experimental support is only available on Android
3030
options.Native.ExperimentalOptions.SessionReplay.OnErrorSampleRate = 1.0;
3131
options.Native.ExperimentalOptions.SessionReplay.SessionSampleRate = 1.0;
32-
options.Native.ExperimentalOptions.SessionReplay.MaskAllImages = false;
33-
options.Native.ExperimentalOptions.SessionReplay.MaskAllText = false;
32+
// Mask all images and text by default. This can be overridden for individual view elements via the
33+
// sentry:SessionReplay.Mask XML attribute (see MainPage.xaml for an example)
34+
options.Native.ExperimentalOptions.SessionReplay.MaskAllImages = true;
35+
options.Native.ExperimentalOptions.SessionReplay.MaskAllText = true;
36+
// Alternatively the masking behaviour for entire classes of VisualElements can be configured here as
37+
// an exception to the default behaviour.
38+
options.Native.ExperimentalOptions.SessionReplay.UnmaskControlsOfType<Button>();
3439
#endif
3540

3641
options.SetBeforeScreenshotCapture((@event, hint) =>

samples/Sentry.Samples.Maui/Platforms/Android/MainActivity.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
#if __ANDROID__
12
using Android.App;
23
using Android.Content.PM;
4+
using Android.OS;
5+
#endif
36

47
namespace Sentry.Samples.Maui;
58

src/Sentry.Maui/AssemblyInfo.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// XML namespaces for custom XAML elements defined in the Sentry.Maui assembly (e.g. Bindable Properties)
2+
[assembly: XmlnsDefinition("http://schemas.sentry.io/maui", "Sentry.Maui")]
3+
[assembly: Microsoft.Maui.Controls.XmlnsPrefix("http://schemas.sentry.io/maui", "sentry")]

src/Sentry.Maui/Internal/MauiButtonEventsBinder.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ public void UnBind(VisualElement element)
2929
}
3030
}
3131

32-
3332
private void OnButtonOnClicked(object? sender, EventArgs _)
3433
=> addBreadcrumbCallback?.Invoke(new(sender, nameof(Button.Clicked)));
3534

src/Sentry.Maui/Internal/MauiEventsBinder.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using Microsoft.Extensions.Options;
2+
using Microsoft.Maui.Platform;
3+
using Sentry.Extensibility;
24

35
namespace Sentry.Maui.Internal;
46

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using Microsoft.Extensions.Options;
2+
using Sentry.Extensibility;
3+
#if __ANDROID__
4+
using View = Android.Views.View;
5+
#endif
6+
7+
namespace Sentry.Maui.Internal;
8+
9+
/// <summary>
10+
/// Masks or unmasks visual elements for session replay recordings
11+
/// </summary>
12+
internal class MauiVisualElementEventsBinder : IMauiElementEventBinder
13+
{
14+
private readonly SentryMauiOptions _options;
15+
16+
public MauiVisualElementEventsBinder(IOptions<SentryMauiOptions> options)
17+
{
18+
_options = options.Value;
19+
}
20+
21+
/// <inheritdoc />
22+
public void Bind(VisualElement element, Action<BreadcrumbEvent> _)
23+
{
24+
element.Loaded += OnElementLoaded;
25+
}
26+
27+
/// <inheritdoc />
28+
public void UnBind(VisualElement element)
29+
{
30+
element.Loaded -= OnElementLoaded;
31+
}
32+
33+
internal void OnElementLoaded(object? sender, EventArgs _)
34+
{
35+
if (sender is not VisualElement element)
36+
{
37+
_options.LogDebug("OnElementLoaded: sender is not a VisualElement");
38+
return;
39+
}
40+
41+
var handler = element.Handler;
42+
if (handler is null)
43+
{
44+
_options.LogDebug("OnElementLoaded: element.Handler is null");
45+
return;
46+
}
47+
48+
#if __ANDROID__
49+
if (element.Handler?.PlatformView is not View nativeView)
50+
{
51+
return;
52+
}
53+
54+
if (_options.Native.ExperimentalOptions.SessionReplay.MaskedControls.FirstOrDefault(maskType => element.GetType().IsAssignableFrom(maskType)) is not null)
55+
{
56+
nativeView.Tag = SessionReplayMaskMode.Mask.ToNativeTag();
57+
_options.LogDebug("OnElementLoaded: Successfully set sentry-mask tag on native view");
58+
}
59+
else if (_options.Native.ExperimentalOptions.SessionReplay.UnmaskedControls.FirstOrDefault(unmaskType => element.GetType().IsAssignableFrom(unmaskType)) is not null)
60+
{
61+
nativeView.Tag = SessionReplayMaskMode.Unmask.ToNativeTag();
62+
_options.LogDebug("OnElementLoaded: Successfully set sentry-unmask tag on native view");
63+
}
64+
#endif
65+
}
66+
}

src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public static MauiAppBuilder UseSentry(this MauiAppBuilder builder,
5757

5858
services.AddSingleton<IMauiElementEventBinder, MauiButtonEventsBinder>();
5959
services.AddSingleton<IMauiElementEventBinder, MauiImageButtonEventsBinder>();
60+
services.AddSingleton<IMauiElementEventBinder, MauiVisualElementEventsBinder>();
6061
services.TryAddSingleton<IMauiEventsBinder, MauiEventsBinder>();
6162

6263
services.AddSentry<SentryMauiOptions>();

src/Sentry.Maui/SentryMauiOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public SentryMauiOptions()
8181
/// if this callback return false the capture will not take place
8282
/// </remarks>
8383
/// <code>
84-
///
84+
///
8585
///options.SetBeforeCapture((@event, hint) =>
8686
///{
8787
/// // Return true to capture or false to prevent the capture

0 commit comments

Comments
 (0)