diff --git a/CHANGELOG.md b/CHANGELOG.md
index d26aa4946..41ac6856e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
- Sentry config is now a scriptable object ([#220](https://github.com/getsentry/sentry-unity/pull/220))
- Unity protocol ([#234](https://github.com/getsentry/sentry-unity/pull/234))
+- Release health integration & Event-listener ([#225](https://github.com/getsentry/sentry-unity/pull/225))
### Fixes
diff --git a/samples/Directory.Build.targets b/samples/Directory.Build.targets
new file mode 100644
index 000000000..d6ad0385c
--- /dev/null
+++ b/samples/Directory.Build.targets
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/Sentry.Unity/ApplicationPauseListener.cs b/src/Sentry.Unity/ApplicationPauseListener.cs
new file mode 100644
index 000000000..668463018
--- /dev/null
+++ b/src/Sentry.Unity/ApplicationPauseListener.cs
@@ -0,0 +1,86 @@
+using System;
+using UnityEngine;
+
+namespace Sentry.Unity
+{
+ ///
+ /// A MonoBehavior used to forward application focus events to subscribers.
+ ///
+ [DefaultExecutionOrder(-900)]
+ internal class ApplicationPauseListener : MonoBehaviour
+ {
+ ///
+ /// Hook to receive an event when the application gains focus.
+ ///
+ /// Listens to OnApplicationFocus for all platforms except Android, where we listen to OnApplicationPause.
+ ///
+ ///
+ public event Action? ApplicationResuming;
+
+ ///
+ /// Hook to receive an event when the application loses focus.
+ ///
+ /// Listens to OnApplicationFocus for all platforms except Android, where we listen to OnApplicationPause.
+ ///
+ ///
+ public event Action? ApplicationPausing;
+
+ // Keeping internal track of running state because OnApplicationPause and OnApplicationFocus get called during startup and would fire false resume events
+ private bool _isRunning = true;
+
+ ///
+ /// To receive Leaving/Resuming events on Android.
+ ///
+ /// On Android, when the on-screen keyboard is enabled, it causes a OnApplicationFocus(false) event.
+ /// Additionally, if you press "Home" at the moment the keyboard is enabled, the OnApplicationFocus() event is
+ /// not called, but OnApplicationPause() is called instead.
+ ///
+ ///
+ ///
+ private void OnApplicationPause(bool pauseStatus)
+ {
+ if (Application.platform != RuntimePlatform.Android)
+ {
+ return;
+ }
+
+ if (pauseStatus && _isRunning)
+ {
+ _isRunning = false;
+ ApplicationPausing?.Invoke();
+ }
+ else if (!pauseStatus && !_isRunning)
+ {
+ _isRunning = true;
+ ApplicationResuming?.Invoke();
+ }
+ }
+
+ ///
+ /// To receive Leaving/Resuming events on all platforms except Android.
+ ///
+ ///
+ private void OnApplicationFocus(bool hasFocus)
+ {
+ // To avoid event duplication on Android since the pause event will be handled via OnApplicationPause
+ if (Application.platform == RuntimePlatform.Android)
+ {
+ return;
+ }
+
+ if (hasFocus && !_isRunning)
+ {
+ _isRunning = true;
+ ApplicationResuming?.Invoke();
+ }
+ else if (!hasFocus && _isRunning)
+ {
+ _isRunning = false;
+ ApplicationPausing?.Invoke();
+ }
+ }
+
+ // The GameObject has to destroy itself since it was created with HideFlags.HideAndDontSave
+ private void OnApplicationQuit() => Destroy(gameObject);
+ }
+}
diff --git a/src/Sentry.Unity/Integrations/SessionIntegration.cs b/src/Sentry.Unity/Integrations/SessionIntegration.cs
new file mode 100644
index 000000000..010349a17
--- /dev/null
+++ b/src/Sentry.Unity/Integrations/SessionIntegration.cs
@@ -0,0 +1,33 @@
+using Sentry.Extensibility;
+using Sentry.Integrations;
+using UnityEngine;
+
+namespace Sentry.Unity.Integrations
+{
+ public class SessionIntegration : ISdkIntegration
+ {
+ public void Register(IHub hub, SentryOptions options)
+ {
+ if (!options.AutoSessionTracking)
+ {
+ return;
+ }
+
+ options.DiagnosticLogger?.LogDebug("Registering Session integration.");
+
+ // HideFlags.HideAndDontSave hides the GameObject in the hierarchy and prevents changing of scenes from destroying it
+ var gameListenerObject = new GameObject("SentryListener") {hideFlags = HideFlags.HideAndDontSave};
+ var gameListener = gameListenerObject.AddComponent();
+ gameListener.ApplicationResuming += () =>
+ {
+ options.DiagnosticLogger?.LogDebug("Resuming session.");
+ hub.ResumeSession();
+ };
+ gameListener.ApplicationPausing += () =>
+ {
+ options.DiagnosticLogger?.LogDebug("Pausing session.");
+ hub.PauseSession();
+ };
+ }
+ }
+}
diff --git a/src/Sentry.Unity/Integrations/UnityApplicationLoggingIntegration.cs b/src/Sentry.Unity/Integrations/UnityApplicationLoggingIntegration.cs
index e683980f0..cd61c47ed 100644
--- a/src/Sentry.Unity/Integrations/UnityApplicationLoggingIntegration.cs
+++ b/src/Sentry.Unity/Integrations/UnityApplicationLoggingIntegration.cs
@@ -80,6 +80,7 @@ private void OnQuitting()
// Note: On Windows Store Apps and Windows Phone 8.1 there is no application quit event. Consider using OnApplicationFocus event when focusStatus equals false.
// Note: On WebGL it is not possible to implement OnApplicationQuit due to nature of the browser tabs closing.
_application.LogMessageReceived -= OnLogMessageReceived;
+ _hub?.EndSession();
_hub?.FlushAsync(_sentryOptions?.ShutdownTimeout ?? TimeSpan.FromSeconds(1)).GetAwaiter().GetResult();
}
diff --git a/src/Sentry.Unity/SentryOptionsUtility.cs b/src/Sentry.Unity/SentryOptionsUtility.cs
index 65d0adf94..314ca1296 100644
--- a/src/Sentry.Unity/SentryOptionsUtility.cs
+++ b/src/Sentry.Unity/SentryOptionsUtility.cs
@@ -1,6 +1,5 @@
-using Sentry.Extensibility;
+using System;
using Sentry.Unity.Integrations;
-using UnityEngine;
namespace Sentry.Unity
{
@@ -12,6 +11,7 @@ public static void SetDefaults(SentryUnityOptions options, IApplication? applica
options.Enabled = true;
options.Dsn = null;
+ options.AutoSessionTracking = true;
options.CaptureInEditor = true;
options.RequestBodyCompressionLevel = CompressionLevelWithAuto.NoCompression;
options.AttachStacktrace = false;
@@ -26,7 +26,7 @@ public static void SetDefaults(SentryUnityOptions options, IApplication? applica
options.CacheDirectoryPath = application.PersistentDataPath;
options.Debug = true;
- options.DebugOnlyInEditor = true;
+ options.DebugOnlyInEditor = false;
options.DiagnosticLevel = SentryLevel.Warning;
TryAttachLogger(options, application);
@@ -46,7 +46,7 @@ public static void SetDefaults(ScriptableSentryUnityOptions options)
options.EnableOfflineCaching = true;
options.Debug = true;
- options.DebugOnlyInEditor = true;
+ options.DebugOnlyInEditor = false;
options.DiagnosticLevel = SentryLevel.Warning;
}
diff --git a/src/Sentry.Unity/SentryUnity.cs b/src/Sentry.Unity/SentryUnity.cs
index 2435f324f..e385b733d 100644
--- a/src/Sentry.Unity/SentryUnity.cs
+++ b/src/Sentry.Unity/SentryUnity.cs
@@ -1,7 +1,6 @@
using System;
using System.ComponentModel;
using Sentry.Extensibility;
-using UnityEngine;
namespace Sentry.Unity
{
diff --git a/src/Sentry.Unity/SentryUnityOptions.cs b/src/Sentry.Unity/SentryUnityOptions.cs
index 520a63f11..3971e9cc6 100644
--- a/src/Sentry.Unity/SentryUnityOptions.cs
+++ b/src/Sentry.Unity/SentryUnityOptions.cs
@@ -76,6 +76,7 @@ public SentryUnityOptions()
this.AddIntegration(new UnityApplicationLoggingIntegration());
this.AddIntegration(new UnityBeforeSceneLoadIntegration());
this.AddIntegration(new SceneManagerIntegration());
+ this.AddIntegration(new SessionIntegration());
}
public override string ToString()
diff --git a/src/sentry-dotnet b/src/sentry-dotnet
index 9f6943f83..4e835a344 160000
--- a/src/sentry-dotnet
+++ b/src/sentry-dotnet
@@ -1 +1 @@
-Subproject commit 9f6943f83afca9c2bc8f6cd5759b8db661b1c84a
+Subproject commit 4e835a3440ba93c1cb8532eed433545d69e55b7b
diff --git a/test/Sentry.Unity.Tests/Stubs/TestHub.cs b/test/Sentry.Unity.Tests/Stubs/TestHub.cs
index 1a65a2094..2e62e7fec 100644
--- a/test/Sentry.Unity.Tests/Stubs/TestHub.cs
+++ b/test/Sentry.Unity.Tests/Stubs/TestHub.cs
@@ -35,20 +35,16 @@ public void CaptureTransaction(Transaction transaction)
public void CaptureSession(SessionUpdate sessionUpdate)
{
- throw new NotImplementedException();
}
public Task FlushAsync(TimeSpan timeout)
{
- throw new NotImplementedException();
+ return Task.CompletedTask;
}
public void ConfigureScope(Action configureScope) => _configureScopeCalls.Add(configureScope);
- public Task ConfigureScopeAsync(Func configureScope)
- {
- throw new NotImplementedException();
- }
+ public Task ConfigureScopeAsync(Func configureScope) => Task.CompletedTask;
public void BindClient(ISentryClient client)
{
@@ -93,12 +89,22 @@ public void BindException(Exception exception, ISpan span)
public void StartSession()
{
- throw new NotImplementedException();
+ // TODO: test sessions
+ }
+
+ public void PauseSession()
+ {
+ // TODO: test sessions
+ }
+
+ public void ResumeSession()
+ {
+ // TODO: test sessions
}
public void EndSession(SessionEndStatus status = SessionEndStatus.Exited)
{
- throw new NotImplementedException();
+ // TODO: test sessions
}
}
}