Skip to content

Commit e8a65cf

Browse files
feat: Support custom masking for SessionReplay in MAUI Android apps
1 parent 3f9dd96 commit e8a65cf

File tree

4 files changed

+69
-2
lines changed

4 files changed

+69
-2
lines changed

samples/Sentry.Samples.Maui/MainPage.xaml

Lines changed: 6 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>
@@ -38,13 +39,15 @@
3839
<Button
3940
x:Name="ThrowAndCaptureBtn"
4041
Text="Throw and Capture Exception"
42+
sentry:SessionReplay.Mask="sentry-unmask"
4143
SemanticProperties.Hint="Throws an exception and captures it."
4244
Clicked="OnCapturedExceptionClicked"
4345
HorizontalOptions="Center" />
4446

4547
<Button
4648
x:Name="ThrowUnhandledBtn"
4749
Text="Throw Unhandled .NET Exception (Crash)"
50+
sentry:SessionReplay.Mask="sentry-unmask"
4851
SemanticProperties.Hint="Throws an unhandled .NET exception, crashing the app."
4952
Clicked="OnUnhandledExceptionClicked"
5053
HorizontalOptions="Center" />
@@ -59,20 +62,23 @@
5962
<Button
6063
x:Name="JavaCrashBtn"
6164
Text="Throw Java Exception (Crash)"
65+
sentry:SessionReplay.Mask="sentry-unmask"
6266
SemanticProperties.Hint="Throws an unhandled Java exception, crashing the app."
6367
Clicked="OnJavaCrashClicked"
6468
HorizontalOptions="Center" />
6569

6670
<Button
6771
x:Name="NativeCrashBtn"
6872
Text="Cause Native Crash"
73+
sentry:SessionReplay.Mask="sentry-unmask"
6974
SemanticProperties.Hint="Performs an invalid native operation, crashing the app."
7075
Clicked="OnNativeCrashClicked"
7176
HorizontalOptions="Center" />
7277

7378
<Button
7479
x:Name="FeedbackBtn"
7580
Text="Submit Feedback"
81+
sentry:SessionReplay.Mask="sentry-unmask"
7682
SemanticProperties.Hint="Provides a form that can be used to capture open feedback from the user."
7783
Clicked="OnFeedbackClicked"
7884
HorizontalOptions="Center" />

samples/Sentry.Samples.Maui/MauiProgram.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ public static MauiApp CreateMauiApp()
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.
33+
// This can be overridden for individual view elements (see MainPage.xaml for an example)
34+
options.Native.ExperimentalOptions.SessionReplay.MaskAllImages = true;
35+
options.Native.ExperimentalOptions.SessionReplay.MaskAllText = true;
3436
#endif
3537

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

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/SessionReplay.cs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#if __ANDROID__
2+
using View = Android.Views.View;
3+
#endif
4+
5+
namespace Sentry.Maui;
6+
7+
/// <summary>
8+
/// Contains custom <see cref="BindableProperty"/> definitions used to control the behaviour of the Sentry SessionReplay
9+
/// feature in MAUI apps.
10+
/// </summary>
11+
public static class SessionReplay
12+
{
13+
/// <summary>
14+
/// Mask can be used to either unmask or mask a view.
15+
/// </summary>
16+
public static readonly BindableProperty MaskProperty =
17+
BindableProperty.CreateAttached(
18+
"Mask",
19+
typeof(string),
20+
typeof(SessionReplay),
21+
defaultValue: "sentry-mask", // default: masked
22+
propertyChanged: OnMaskChanged);
23+
24+
/// <summary>
25+
/// Gets the value of the Mask property for a view
26+
/// </summary>
27+
public static string GetMask(BindableObject view) => (string)view.GetValue(MaskProperty);
28+
29+
/// <summary>
30+
/// Sets the value of the Mask property for a view. .
31+
/// </summary>
32+
/// <param name="view">The view element to mask or unmask</param>
33+
/// <param name="value">The value to assign. Can be either "sentry-mask" or "sentry-unmask".</param>
34+
public static void SetMask(BindableObject view, string value) => view.SetValue(MaskProperty, value);
35+
36+
private static void OnMaskChanged(BindableObject bindable, object oldValue, object newValue)
37+
{
38+
#if __ANDROID__
39+
if (bindable is VisualElement ve)
40+
{
41+
ve.HandlerChanged += (s, e) =>
42+
{
43+
if (ve.Handler?.PlatformView is View nativeView && newValue is string maskSetting)
44+
{
45+
if (string.IsNullOrEmpty(maskSetting))
46+
{
47+
return;
48+
}
49+
// "sentry-unmask" if true; remove tag if false
50+
nativeView.Tag = maskSetting;
51+
}
52+
};
53+
}
54+
#endif
55+
}
56+
}

0 commit comments

Comments
 (0)