diff --git a/Directory.Build.props b/Directory.Build.props
index 3fb696d..c36bf80 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -9,7 +9,7 @@
-
+
diff --git a/Samples/Sample.Xamarin.Core/Sample.Xamarin.Core.csproj b/Samples/Sample.Xamarin.Core/Sample.Xamarin.Core.csproj
index dfce5ed..f457355 100644
--- a/Samples/Sample.Xamarin.Core/Sample.Xamarin.Core.csproj
+++ b/Samples/Sample.Xamarin.Core/Sample.Xamarin.Core.csproj
@@ -15,7 +15,7 @@
-
+
diff --git a/Samples/Sample.Xamarin.Droid/MainActivity.cs b/Samples/Sample.Xamarin.Droid/MainActivity.cs
index 760a51d..c4e8925 100644
--- a/Samples/Sample.Xamarin.Droid/MainActivity.cs
+++ b/Samples/Sample.Xamarin.Droid/MainActivity.cs
@@ -19,9 +19,8 @@ protected override void OnCreate(Bundle savedInstanceState)
{
options.Dsn = "https://5a193123a9b841bc8d8e42531e7242a1@o447951.ingest.sentry.io/5560112";
options.AddXamarinFormsIntegration();
-#if DEBUG
options.Debug = true;
-#endif
+ options.AttachScreenshots = true;
});
TabLayoutResource = Resource.Layout.Tabbar;
diff --git a/Samples/Sample.Xamarin.Droid/Sample.Xamarin.Droid.csproj b/Samples/Sample.Xamarin.Droid/Sample.Xamarin.Droid.csproj
index 9c6cdc0..4f354c6 100644
--- a/Samples/Sample.Xamarin.Droid/Sample.Xamarin.Droid.csproj
+++ b/Samples/Sample.Xamarin.Droid/Sample.Xamarin.Droid.csproj
@@ -67,7 +67,7 @@
4.3.4
-
+
diff --git a/Samples/Sample.Xamarin.UWP/App.xaml.cs b/Samples/Sample.Xamarin.UWP/App.xaml.cs
index c1a41c2..7207b16 100644
--- a/Samples/Sample.Xamarin.UWP/App.xaml.cs
+++ b/Samples/Sample.Xamarin.UWP/App.xaml.cs
@@ -1,4 +1,5 @@
using Sentry;
+using Sentry.Infrastructure;
using System;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
@@ -16,9 +17,9 @@ protected override void OnLaunched(LaunchActivatedEventArgs e)
{
options.Dsn = "https://5a193123a9b841bc8d8e42531e7242a1@o447951.ingest.sentry.io/5560112";
options.AddXamarinFormsIntegration();
-#if DEBUG
options.Debug = true;
-#endif
+ options.DiagnosticLogger = new TraceDiagnosticLogger(SentryLevel.Debug);
+ options.AttachScreenshots = true;
});
Frame rootFrame = Window.Current.Content as Frame;
diff --git a/Samples/Sample.Xamarin.UWP/Package.appxmanifest b/Samples/Sample.Xamarin.UWP/Package.appxmanifest
index dd692ba..eee947f 100644
--- a/Samples/Sample.Xamarin.UWP/Package.appxmanifest
+++ b/Samples/Sample.Xamarin.UWP/Package.appxmanifest
@@ -4,7 +4,8 @@
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
- IgnorableNamespaces="uap mp">
+ xmlns:uap6="http://schemas.microsoft.com/appx/manifest/uap/windows10/6"
+ IgnorableNamespaces="uap mp uap6">
+
\ No newline at end of file
diff --git a/Samples/Sample.Xamarin.UWP/Sample.Xamarin.UWP.csproj b/Samples/Sample.Xamarin.UWP/Sample.Xamarin.UWP.csproj
index bdb5eef..963bc4c 100644
--- a/Samples/Sample.Xamarin.UWP/Sample.Xamarin.UWP.csproj
+++ b/Samples/Sample.Xamarin.UWP/Sample.Xamarin.UWP.csproj
@@ -159,7 +159,7 @@
2.0.0.9
- 1.5.3.2
+ 1.6.1
4.8.0.1451
@@ -196,4 +196,4 @@
-->
-
\ No newline at end of file
+
diff --git a/Samples/Sample.Xamarin.iOS/Sample.Xamarin.iOS.csproj b/Samples/Sample.Xamarin.iOS/Sample.Xamarin.iOS.csproj
index 1664f4c..880715d 100644
--- a/Samples/Sample.Xamarin.iOS/Sample.Xamarin.iOS.csproj
+++ b/Samples/Sample.Xamarin.iOS/Sample.Xamarin.iOS.csproj
@@ -132,7 +132,7 @@
2.0.0.9
-
+
diff --git a/Src/Sentry.Xamarin/Extensions/SentryXamarinOptionsExtensions.cs b/Src/Sentry.Xamarin/Extensions/SentryXamarinOptionsExtensions.cs
index 45cd7b7..2648f86 100644
--- a/Src/Sentry.Xamarin/Extensions/SentryXamarinOptionsExtensions.cs
+++ b/Src/Sentry.Xamarin/Extensions/SentryXamarinOptionsExtensions.cs
@@ -2,6 +2,7 @@
using Xamarin.Essentials;
using Sentry.Xamarin.Internals;
using Sentry.Internals.Session;
+using Sentry.Internals.Device.Screenshot;
namespace Sentry
{
@@ -62,6 +63,16 @@ internal static void RegisterXamarinEventProcessors(this SentryXamarinOptions op
#endif
}
+ internal static void RegisterScreenshotEventProcessor(this SentryXamarinOptions options)
+ {
+#if NATIVE_PROCESSOR && !UAP10_0_16299
+ if (options.AttachScreenshots)
+ {
+ options.AddEventProcessor(new ScreenshotEventProcessor(options));
+ }
+#endif
+ }
+
internal static bool RegisterNativeIntegrations(this SentryXamarinOptions options)
{
if (options.NativeIntegrationEnabled)
diff --git a/Src/Sentry.Xamarin/Internals/Device/Screenshot/ScreenshotAttachment.cs b/Src/Sentry.Xamarin/Internals/Device/Screenshot/ScreenshotAttachment.cs
new file mode 100644
index 0000000..cd6bef6
--- /dev/null
+++ b/Src/Sentry.Xamarin/Internals/Device/Screenshot/ScreenshotAttachment.cs
@@ -0,0 +1,26 @@
+using System;
+using System.IO;
+
+namespace Sentry.Internals.Device.Screenshot
+{
+ internal class ScreenshotAttachment : Attachment
+ {
+ public ScreenshotAttachment(ScreenshotAttachmentContent content)
+ : this(
+ AttachmentType.Default,
+ content,
+ "screenshot",
+ "image/jpeg")
+ {
+ }
+
+ private ScreenshotAttachment(
+ AttachmentType type,
+ IAttachmentContent content,
+ string fileName,
+ string? contentType)
+ : base(type, content, fileName, contentType)
+ {
+ }
+ }
+}
diff --git a/Src/Sentry.Xamarin/Internals/Device/Screenshot/ScreenshotAttachmentContent.cs b/Src/Sentry.Xamarin/Internals/Device/Screenshot/ScreenshotAttachmentContent.cs
new file mode 100644
index 0000000..db87381
--- /dev/null
+++ b/Src/Sentry.Xamarin/Internals/Device/Screenshot/ScreenshotAttachmentContent.cs
@@ -0,0 +1,48 @@
+using System.IO;
+
+namespace Sentry.Internals.Device.Screenshot
+{
+ internal class ScreenshotAttachmentContent : IAttachmentContent
+ {
+ private byte[] _data { get; set; }
+
+ private bool _wasRead { get; set; }
+
+ ///
+ /// The content ctor
+ ///
+ /// A Ready once stream containing the image data.
+ public ScreenshotAttachmentContent(Stream stream)
+ {
+ SetNewData(stream);
+ }
+
+ public void SetNewData(Stream stream)
+ {
+ _data = new byte[stream.Length];
+ stream.Read(_data, 0, _data.Length);
+ }
+
+ ///
+ /// Inform is the content was previously read.
+ /// By calling this function you will reset the read state to false and return the current state.
+ ///
+ /// true if the content was read.
+ public bool ResetWasRead()
+ {
+ var read = _wasRead;
+ _wasRead = false;
+ return read;
+ }
+
+ ///
+ /// Get a stream from the attachment data.
+ ///
+ /// A Memory stream containing the data.
+ public Stream GetStream()
+ {
+ _wasRead = true;
+ return new MemoryStream(_data);
+ }
+ }
+}
diff --git a/Src/Sentry.Xamarin/Internals/Device/Screenshot/ScreenshotEventProcessor.droid.ios.cs b/Src/Sentry.Xamarin/Internals/Device/Screenshot/ScreenshotEventProcessor.droid.ios.cs
new file mode 100644
index 0000000..3f3826a
--- /dev/null
+++ b/Src/Sentry.Xamarin/Internals/Device/Screenshot/ScreenshotEventProcessor.droid.ios.cs
@@ -0,0 +1,53 @@
+using Sentry.Extensibility;
+using Xamarin.Essentials;
+using System.IO;
+using System;
+
+namespace Sentry.Internals.Device.Screenshot
+{
+ internal class ScreenshotEventProcessor : ISentryEventProcessor
+ {
+ private ScreenshotAttachmentContent? _screenshot { get; set; }
+ private SentryXamarinOptions _options { get; }
+
+ public ScreenshotEventProcessor(SentryXamarinOptions options) => _options = options;
+
+ public SentryEvent? Process(SentryEvent @event)
+ {
+ try
+ {
+ if (_options.SessionLogger?.IsBackground() == false)
+ {
+ var stream = Capture();
+ if (stream != null)
+ {
+ /* Create a new attachment if no screenshot attachment was found.
+ * If there's an attachment content but it wasnt read during the last event processing
+ * Assume it was cleared and create a new one.
+ */
+ if (_screenshot?.ResetWasRead() != true)
+ {
+ _screenshot = new ScreenshotAttachmentContent(stream);
+ SentrySdk.ConfigureScope(s => s.AddAttachment(new ScreenshotAttachment(_screenshot)));
+ }
+ else
+ {
+ _screenshot.SetNewData(stream);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ _options.DiagnosticLogger?.LogError("Failed to capture a screenshot", ex);
+ }
+ return @event;
+ }
+
+ private Stream Capture()
+ {
+ var screenStream = global::Xamarin.Essentials.Screenshot.CaptureAsync().ConfigureAwait(false).GetAwaiter().GetResult();
+ return screenStream.OpenReadAsync(ScreenshotFormat.Jpeg).ConfigureAwait(false).GetAwaiter().GetResult();
+ }
+ }
+}
diff --git a/Src/Sentry.Xamarin/Internals/Session/DeviceActiveLogger.droid.cs b/Src/Sentry.Xamarin/Internals/Session/DeviceActiveLogger.droid.cs
index 6e4d976..7c31543 100644
--- a/Src/Sentry.Xamarin/Internals/Session/DeviceActiveLogger.droid.cs
+++ b/Src/Sentry.Xamarin/Internals/Session/DeviceActiveLogger.droid.cs
@@ -9,6 +9,12 @@ internal class DeviceActiveLogger : IDeviceActiveLogger
///
private bool IsPaused;
+ ///
+ /// Informs if the device is on background.
+ ///
+ /// True if the app is on background, otherwise false
+ public bool IsBackground() => IsPaused;
+
///
public void StatePaused()
{
diff --git a/Src/Sentry.Xamarin/Internals/Session/DeviceActiveLogger.ios.cs b/Src/Sentry.Xamarin/Internals/Session/DeviceActiveLogger.ios.cs
deleted file mode 100644
index b71257e..0000000
--- a/Src/Sentry.Xamarin/Internals/Session/DeviceActiveLogger.ios.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Sentry.Internals.Session
-{
- internal class DeviceActiveLogger : IDeviceActiveLogger
- {
- ///
- public void StatePaused() => SentrySdk.PauseSession();
-
- ///
- public void StateResumed() => SentrySdk.ResumeSession();
- }
-}
diff --git a/Src/Sentry.Xamarin/Internals/Session/DeviceActiveLogger.ios.uwp.cs b/Src/Sentry.Xamarin/Internals/Session/DeviceActiveLogger.ios.uwp.cs
new file mode 100644
index 0000000..31cf219
--- /dev/null
+++ b/Src/Sentry.Xamarin/Internals/Session/DeviceActiveLogger.ios.uwp.cs
@@ -0,0 +1,27 @@
+namespace Sentry.Internals.Session
+{
+ internal class DeviceActiveLogger : IDeviceActiveLogger
+ {
+ private bool _isPaused { get; set; } = true;
+
+ ///
+ /// Informs if the device is on background.
+ ///
+ /// True if the app is on background, otherwise false
+ public bool IsBackground() => _isPaused;
+
+ ///
+ public void StatePaused()
+ {
+ _isPaused = true;
+ SentrySdk.PauseSession();
+ }
+
+ ///
+ public void StateResumed()
+ {
+ _isPaused = false;
+ SentrySdk.ResumeSession();
+ }
+ }
+}
diff --git a/Src/Sentry.Xamarin/Internals/Session/DeviceActiveLogger.uwp.cs b/Src/Sentry.Xamarin/Internals/Session/DeviceActiveLogger.uwp.cs
deleted file mode 100644
index b71257e..0000000
--- a/Src/Sentry.Xamarin/Internals/Session/DeviceActiveLogger.uwp.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Sentry.Internals.Session
-{
- internal class DeviceActiveLogger : IDeviceActiveLogger
- {
- ///
- public void StatePaused() => SentrySdk.PauseSession();
-
- ///
- public void StateResumed() => SentrySdk.ResumeSession();
- }
-}
diff --git a/Src/Sentry.Xamarin/Internals/Session/IDeviceActiveLogger.cs b/Src/Sentry.Xamarin/Internals/Session/IDeviceActiveLogger.cs
index 774569d..72bf24f 100644
--- a/Src/Sentry.Xamarin/Internals/Session/IDeviceActiveLogger.cs
+++ b/Src/Sentry.Xamarin/Internals/Session/IDeviceActiveLogger.cs
@@ -5,5 +5,7 @@ internal interface IDeviceActiveLogger
void StatePaused();
void StateResumed();
+
+ bool IsBackground();
}
}
diff --git a/Src/Sentry.Xamarin/Sentry.Xamarin.csproj b/Src/Sentry.Xamarin/Sentry.Xamarin.csproj
index 4a21913..bddf03f 100644
--- a/Src/Sentry.Xamarin/Sentry.Xamarin.csproj
+++ b/Src/Sentry.Xamarin/Sentry.Xamarin.csproj
@@ -30,37 +30,37 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
-
+
-
+
-
+
-
+
diff --git a/Src/Sentry.Xamarin/SentryXamarin.cs b/Src/Sentry.Xamarin/SentryXamarin.cs
index 666cb24..db3f607 100644
--- a/Src/Sentry.Xamarin/SentryXamarin.cs
+++ b/Src/Sentry.Xamarin/SentryXamarin.cs
@@ -37,6 +37,7 @@ public static void Init(SentryXamarinOptions options)
options.RegisterNativeActivityStatus();
options.RegisterNativeIntegrations();
options.RegisterXamarinEventProcessors();
+ options.RegisterScreenshotEventProcessor();
options.RegisterXamarinInAppExclude();
options.ProtocolPackageName ??= ProtocolPackageName;
SentrySdk.Init(options);
diff --git a/Src/Sentry.Xamarin/SentryXamarinOptions.cs b/Src/Sentry.Xamarin/SentryXamarinOptions.cs
index 7b51bcd..ee36d5a 100644
--- a/Src/Sentry.Xamarin/SentryXamarinOptions.cs
+++ b/Src/Sentry.Xamarin/SentryXamarinOptions.cs
@@ -8,6 +8,19 @@ namespace Sentry
///
public class SentryXamarinOptions : SentryOptions
{
+ ///
+ /// Attaches screenshots from the app to events automatically whenever possible.
+ ///
+ ///
+ /// Pii information might be exposed by activating this feature.
+ ///
+ public bool AttachScreenshots { get; set; }
+
+ ///
+ /// Define the range of time that duplicated internal breadcrumbs will be ignored.
+ ///
+ public int InternalBreadcrumbDuplicationTimeSpan { get; set; } = 2;
+
internal bool XamarinLoggerEnabled { get; set; } = true;
internal bool NativeIntegrationEnabled { get; set; } = true;
internal bool InternalCacheEnabled { get; set; } = true;
@@ -22,10 +35,7 @@ public class SentryXamarinOptions : SentryOptions
/// device's app status.
///
internal IDeviceActiveLogger SessionLogger { get; set; }
- ///
- /// Define the range of time that duplicated internal breadcrumbs will be ignored.
- ///
- public int InternalBreadcrumbDuplicationTimeSpan { get; set; } = 2;
+
internal Breadcrumb LastInternalBreadcrumb {get;set;}
///
@@ -35,6 +45,7 @@ public SentryXamarinOptions()
{
IsEnvironmentUser = false;
AutoSessionTracking = true;
+ IsGlobalModeEnabled = true;
}
}
}