diff --git a/CHANGELOG.md b/CHANGELOG.md
index 749a3f04..68ae979a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## Unreleased
+
+- Remove need for context in Sentry.init for Android ([#117](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/117))
+
## 0.2.1
### Fixes
diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt
index 26d9dbe2..79c228a9 100644
--- a/buildSrc/src/main/java/Config.kt
+++ b/buildSrc/src/main/java/Config.kt
@@ -53,6 +53,7 @@ object Config {
val roboelectric = "org.robolectric:robolectric:4.9"
val junitKtx = "androidx.test.ext:junit-ktx:1.1.5"
+ val mockitoCore = "org.mockito:mockito-core:5.4.0"
}
object Android {
diff --git a/sentry-kotlin-multiplatform/build.gradle.kts b/sentry-kotlin-multiplatform/build.gradle.kts
index fbc52077..369ee4c9 100644
--- a/sentry-kotlin-multiplatform/build.gradle.kts
+++ b/sentry-kotlin-multiplatform/build.gradle.kts
@@ -67,6 +67,7 @@ kotlin {
dependencies {
implementation(Config.TestLibs.roboelectric)
implementation(Config.TestLibs.junitKtx)
+ implementation(Config.TestLibs.mockitoCore)
}
}
val jvmMain by getting
diff --git a/sentry-kotlin-multiplatform/src/androidMain/AndroidManifest.xml b/sentry-kotlin-multiplatform/src/androidMain/AndroidManifest.xml
index 8c41501c..f0aaedea 100644
--- a/sentry-kotlin-multiplatform/src/androidMain/AndroidManifest.xml
+++ b/sentry-kotlin-multiplatform/src/androidMain/AndroidManifest.xml
@@ -8,7 +8,14 @@
android:name="io.sentry.android.core.SentryInitProvider"
android:authorities="${applicationId}.SentryInitProvider"
android:exported="false"
- tools:node="remove"/>
+ tools:node="remove">
+
+
+
diff --git a/sentry-kotlin-multiplatform/src/androidMain/kotlin/io/sentry/kotlin/multiplatform/SentryInit.android.kt b/sentry-kotlin-multiplatform/src/androidMain/kotlin/io/sentry/kotlin/multiplatform/SentryInit.android.kt
index 8777b6eb..da017c2c 100644
--- a/sentry-kotlin-multiplatform/src/androidMain/kotlin/io/sentry/kotlin/multiplatform/SentryInit.android.kt
+++ b/sentry-kotlin-multiplatform/src/androidMain/kotlin/io/sentry/kotlin/multiplatform/SentryInit.android.kt
@@ -1,15 +1,72 @@
package io.sentry.kotlin.multiplatform
+import android.content.ContentProvider
+import android.content.ContentValues
import android.content.Context
+import android.database.Cursor
+import android.net.Uri
import io.sentry.android.core.SentryAndroid
import io.sentry.kotlin.multiplatform.extensions.toAndroidSentryOptionsCallback
-internal actual fun initSentry(context: Context?, configuration: OptionsConfiguration) {
+internal actual fun initSentry(configuration: OptionsConfiguration) {
val options = SentryOptions()
configuration.invoke(options)
- context?.let {
- SentryAndroid.init(it, options.toAndroidSentryOptionsCallback())
- }
+ SentryAndroid.init(applicationContext, options.toAndroidSentryOptionsCallback())
}
+internal lateinit var applicationContext: Context
+ private set
+
public actual typealias Context = Context
+
+/**
+ * A ContentProvider that does NOT store or provide any data for read or write operations.
+ *
+ * It's only purpose is to retrieve and store the application context in an internal top-level
+ * variable [applicationContext]. The context is used for [SentryAndroid.init].
+ *
+ * This does not allow for overriding the abstract query, insert, update, and delete operations
+ * of the [ContentProvider].
+ */
+internal class SentryContextProvider : ContentProvider() {
+ override fun onCreate(): Boolean {
+ val context = context
+ if (context != null) {
+ applicationContext = context.applicationContext
+ } else {
+ error("Context cannot be null")
+ }
+ return true
+ }
+
+ override fun query(
+ uri: Uri,
+ projection: Array?,
+ selection: String?,
+ selectionArgs: Array?,
+ sortOrder: String?
+ ): Cursor? {
+ error("Not allowed.")
+ }
+
+ override fun getType(uri: Uri): String? {
+ error("Not allowed.")
+ }
+
+ override fun insert(uri: Uri, values: ContentValues?): Uri? {
+ error("Not allowed.")
+ }
+
+ override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int {
+ error("Not allowed.")
+ }
+
+ override fun update(
+ uri: Uri,
+ values: ContentValues?,
+ selection: String?,
+ selectionArgs: Array?
+ ): Int {
+ error("Not allowed.")
+ }
+}
diff --git a/sentry-kotlin-multiplatform/src/androidUnitTest/kotlin/io/sentry/kotlin/multiplatform/BaseSentryTest.kt b/sentry-kotlin-multiplatform/src/androidUnitTest/kotlin/io/sentry/kotlin/multiplatform/BaseSentryTest.kt
index 47356110..87a14629 100644
--- a/sentry-kotlin-multiplatform/src/androidUnitTest/kotlin/io/sentry/kotlin/multiplatform/BaseSentryTest.kt
+++ b/sentry-kotlin-multiplatform/src/androidUnitTest/kotlin/io/sentry/kotlin/multiplatform/BaseSentryTest.kt
@@ -1,15 +1,22 @@
package io.sentry.kotlin.multiplatform
import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
import org.junit.runner.RunWith
+import org.robolectric.Robolectric
+import kotlin.test.BeforeTest
@RunWith(AndroidJUnit4::class)
actual abstract class BaseSentryTest {
actual val platform: String = "Android"
actual val authToken: String? = System.getenv("SENTRY_AUTH_TOKEN")
actual fun sentryInit(optionsConfiguration: OptionsConfiguration) {
- val context = InstrumentationRegistry.getInstrumentation().targetContext
- Sentry.init(context, optionsConfiguration)
+ Sentry.init(optionsConfiguration)
+ }
+
+ @BeforeTest
+ open fun setUp() {
+ // Set up the provider needed for Sentry.init on Android
+ val provider = Robolectric.buildContentProvider(SentryContextProvider::class.java)
+ provider.create()
}
}
diff --git a/sentry-kotlin-multiplatform/src/androidUnitTest/kotlin/io/sentry/kotlin/multiplatform/SentryContextProviderTest.kt b/sentry-kotlin-multiplatform/src/androidUnitTest/kotlin/io/sentry/kotlin/multiplatform/SentryContextProviderTest.kt
new file mode 100644
index 00000000..86f53329
--- /dev/null
+++ b/sentry-kotlin-multiplatform/src/androidUnitTest/kotlin/io/sentry/kotlin/multiplatform/SentryContextProviderTest.kt
@@ -0,0 +1,59 @@
+package io.sentry.kotlin.multiplatform
+
+import android.content.ContentValues
+import android.net.Uri
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+@RunWith(AndroidJUnit4::class)
+class SentryContextProviderTest : BaseSentryTest() {
+ private lateinit var provider: SentryContextProvider
+
+ @Before
+ override fun setUp() {
+ provider = SentryContextProvider()
+ }
+
+ // We create a nested class so this test is executed with the BeforeEach method that initializes
+ // the actual content provider and not just a mock.
+ class SentryContextOnCreateTest : BaseSentryTest() {
+ @Test
+ fun `onCreate initializes applicationContext`() {
+ // Simple call to the lateinit applicationContext to make sure it's initialized
+ applicationContext
+ }
+ }
+
+ fun `create does not throw Exception`() {
+ provider.onCreate()
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun `insert throws Exception`() {
+ val uri = mock(Uri::class.java)
+ val values = ContentValues()
+ provider.insert(uri, values)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun `update throws Exception`() {
+ val uri = mock(Uri::class.java)
+ val values = ContentValues()
+ provider.update(uri, values, null, null)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun `delete throws Exception`() {
+ val uri = mock(Uri::class.java)
+ provider.delete(uri, null, null)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun `getType throws Exception`() {
+ val uri = mock(Uri::class.java)
+ provider.getType(uri)
+ }
+}
diff --git a/sentry-kotlin-multiplatform/src/commonJvmMain/kotlin/io/sentry/kotlin/multiplatform/SentryBridge.jvm.kt b/sentry-kotlin-multiplatform/src/commonJvmMain/kotlin/io/sentry/kotlin/multiplatform/SentryBridge.jvm.kt
index e26dde05..585ae762 100644
--- a/sentry-kotlin-multiplatform/src/commonJvmMain/kotlin/io/sentry/kotlin/multiplatform/SentryBridge.jvm.kt
+++ b/sentry-kotlin-multiplatform/src/commonJvmMain/kotlin/io/sentry/kotlin/multiplatform/SentryBridge.jvm.kt
@@ -9,12 +9,12 @@ import io.sentry.kotlin.multiplatform.protocol.SentryId
import io.sentry.kotlin.multiplatform.protocol.User
import io.sentry.kotlin.multiplatform.protocol.UserFeedback
-internal expect fun initSentry(context: Context? = null, configuration: OptionsConfiguration)
+internal expect fun initSentry(configuration: OptionsConfiguration)
internal actual object SentryBridge {
actual fun init(context: Context, configuration: OptionsConfiguration) {
- initSentry(context, configuration)
+ initSentry(configuration)
}
actual fun init(configuration: OptionsConfiguration) {
diff --git a/sentry-kotlin-multiplatform/src/commonMain/kotlin/io/sentry/kotlin/multiplatform/SentryKMP.kt b/sentry-kotlin-multiplatform/src/commonMain/kotlin/io/sentry/kotlin/multiplatform/SentryKMP.kt
index 549ed698..56f6f39e 100644
--- a/sentry-kotlin-multiplatform/src/commonMain/kotlin/io/sentry/kotlin/multiplatform/SentryKMP.kt
+++ b/sentry-kotlin-multiplatform/src/commonMain/kotlin/io/sentry/kotlin/multiplatform/SentryKMP.kt
@@ -11,6 +11,7 @@ public typealias ScopeCallback = (Scope) -> Unit
public typealias OptionsConfiguration = (SentryOptions) -> Unit
/** The context used for Android initialization. */
+@Deprecated("No longer necessary to initialize Sentry on Android.")
public expect abstract class Context
/** Sentry Kotlin Multiplatform SDK API entry point. */
@@ -24,6 +25,10 @@ public object Sentry {
*/
@OptIn(ExperimentalObjCRefinement::class)
@HiddenFromObjC
+ @Deprecated(
+ "Use init(OptionsConfiguration) instead.",
+ ReplaceWith("Sentry.init(configuration)")
+ )
public fun init(context: Context, configuration: OptionsConfiguration) {
SentryBridge.init(context, configuration)
}
diff --git a/sentry-kotlin-multiplatform/src/jvmMain/kotlin/io/sentry/kotlin/multiplatform/SentryInit.jvm.kt b/sentry-kotlin-multiplatform/src/jvmMain/kotlin/io/sentry/kotlin/multiplatform/SentryInit.jvm.kt
index 836d200e..547712f1 100644
--- a/sentry-kotlin-multiplatform/src/jvmMain/kotlin/io/sentry/kotlin/multiplatform/SentryInit.jvm.kt
+++ b/sentry-kotlin-multiplatform/src/jvmMain/kotlin/io/sentry/kotlin/multiplatform/SentryInit.jvm.kt
@@ -3,7 +3,7 @@ package io.sentry.kotlin.multiplatform
import io.sentry.Sentry
import io.sentry.kotlin.multiplatform.extensions.toJvmSentryOptionsCallback
-internal actual fun initSentry(context: Context?, configuration: OptionsConfiguration) {
+internal actual fun initSentry(configuration: OptionsConfiguration) {
val options = SentryOptions()
configuration.invoke(options)
Sentry.init(options.toJvmSentryOptionsCallback())
diff --git a/sentry-samples/kmp-app-cocoapods/androidApp/src/main/java/sample/kmp/app/android/MainActivity.kt b/sentry-samples/kmp-app-cocoapods/androidApp/src/main/java/sample/kmp/app/android/MainActivity.kt
index 1b693381..388efb17 100644
--- a/sentry-samples/kmp-app-cocoapods/androidApp/src/main/java/sample/kmp/app/android/MainActivity.kt
+++ b/sentry-samples/kmp-app-cocoapods/androidApp/src/main/java/sample/kmp/app/android/MainActivity.kt
@@ -42,7 +42,7 @@ class SentryApplication : Application() {
super.onCreate()
// Initialize Sentry using shared code
- initializeSentry(this)
+ initializeSentry()
// Shared scope across all platforms
configureSentryScope()
diff --git a/sentry-samples/kmp-app-cocoapods/shared/src/commonMain/kotlin/sample.kmp.app/SentrySetup.kt b/sentry-samples/kmp-app-cocoapods/shared/src/commonMain/kotlin/sample.kmp.app/SentrySetup.kt
index 331f1017..ffdd4af6 100644
--- a/sentry-samples/kmp-app-cocoapods/shared/src/commonMain/kotlin/sample.kmp.app/SentrySetup.kt
+++ b/sentry-samples/kmp-app-cocoapods/shared/src/commonMain/kotlin/sample.kmp.app/SentrySetup.kt
@@ -1,7 +1,6 @@
package sample.kmp.app
import io.sentry.kotlin.multiplatform.Attachment
-import io.sentry.kotlin.multiplatform.Context
import io.sentry.kotlin.multiplatform.HttpStatusCodeRange
import io.sentry.kotlin.multiplatform.OptionsConfiguration
import io.sentry.kotlin.multiplatform.Sentry
@@ -24,15 +23,6 @@ fun configureSentryScope() {
* Initializes Sentry with given options.
* Make sure to hook this into your native platforms as early as possible
*/
-fun initializeSentry(context: Context) {
- Sentry.init(context, optionsConfiguration())
-}
-
-/**
- * Convenience initializer for Cocoa targets.
- * Kotlin -> ObjC doesn't support default parameters (yet).
- * Otherwise, you would need to do this: AppSetupKt.initializeSentry(context: nil) in Swift.
- */
fun initializeSentry() {
Sentry.init(optionsConfiguration())
}
diff --git a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/MainApp.kt b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/MainApp.kt
index 1d60baaa..05ad4a62 100644
--- a/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/MainApp.kt
+++ b/sentry-samples/kmp-app-mvvm-di/androidApp/src/main/kotlin/sentry/kmp/demo/android/MainApp.kt
@@ -4,14 +4,14 @@ import android.app.Application
import android.content.Context
import org.koin.dsl.module
import sentry.kmp.demo.initKoin
-import sentry.kmp.demo.sentry.initSentry
+import sentry.kmp.demo.sentry.initializeSentry
class MainApp : Application() {
override fun onCreate() {
super.onCreate()
- initSentry(this)
+ initializeSentry()
initKoin(
module {
diff --git a/sentry-samples/kmp-app-mvvm-di/iosApp/AppDelegate.swift b/sentry-samples/kmp-app-mvvm-di/iosApp/AppDelegate.swift
index f863295a..cc0ba4c7 100644
--- a/sentry-samples/kmp-app-mvvm-di/iosApp/AppDelegate.swift
+++ b/sentry-samples/kmp-app-mvvm-di/iosApp/AppDelegate.swift
@@ -11,7 +11,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
startKoin()
- SentrySetupKt.start()
+ SentrySetupKt.initializeSentry()
let viewController = UIHostingController(rootView: HomeScreen())
diff --git a/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/sentry/SentrySetup.kt b/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/sentry/SentrySetup.kt
index 920aa3a2..60f1f6e1 100644
--- a/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/sentry/SentrySetup.kt
+++ b/sentry-samples/kmp-app-mvvm-di/shared/src/commonMain/kotlin/sentry.kmp.demo/sentry/SentrySetup.kt
@@ -1,11 +1,8 @@
package sentry.kmp.demo.sentry
import io.sentry.kotlin.multiplatform.Attachment
-import io.sentry.kotlin.multiplatform.Context
import io.sentry.kotlin.multiplatform.OptionsConfiguration
import io.sentry.kotlin.multiplatform.Sentry
-import kotlin.experimental.ExperimentalObjCRefinement
-import kotlin.native.HiddenFromObjC
/** Shared options configuration */
private val optionsConfiguration: OptionsConfiguration = {
@@ -31,18 +28,7 @@ private val optionsConfiguration: OptionsConfiguration = {
* Initializes Sentry with given options.
* Make sure to hook this into your native platforms as early as possible
*/
-@OptIn(ExperimentalObjCRefinement::class)
-@HiddenFromObjC
-fun initSentry(context: Context) {
- Sentry.init(context, optionsConfiguration)
- configureSentryScope()
-}
-
-/**
- * Convenience initializer for Cocoa targets.
- * Kotlin -> ObjC doesn't support default parameters (yet).
- */
-fun start() {
+fun initializeSentry() {
Sentry.init(optionsConfiguration)
configureSentryScope()
}
diff --git a/sentry-samples/kmp-app-spm/androidApp/src/main/java/sample/kmp/app/android/MainActivity.kt b/sentry-samples/kmp-app-spm/androidApp/src/main/java/sample/kmp/app/android/MainActivity.kt
index 1b693381..388efb17 100644
--- a/sentry-samples/kmp-app-spm/androidApp/src/main/java/sample/kmp/app/android/MainActivity.kt
+++ b/sentry-samples/kmp-app-spm/androidApp/src/main/java/sample/kmp/app/android/MainActivity.kt
@@ -42,7 +42,7 @@ class SentryApplication : Application() {
super.onCreate()
// Initialize Sentry using shared code
- initializeSentry(this)
+ initializeSentry()
// Shared scope across all platforms
configureSentryScope()
diff --git a/sentry-samples/kmp-app-spm/shared/src/commonMain/kotlin/sample.kmp.app/SentrySetup.kt b/sentry-samples/kmp-app-spm/shared/src/commonMain/kotlin/sample.kmp.app/SentrySetup.kt
index c094d11d..be7e6e27 100644
--- a/sentry-samples/kmp-app-spm/shared/src/commonMain/kotlin/sample.kmp.app/SentrySetup.kt
+++ b/sentry-samples/kmp-app-spm/shared/src/commonMain/kotlin/sample.kmp.app/SentrySetup.kt
@@ -1,7 +1,6 @@
package sample.kmp.app
import io.sentry.kotlin.multiplatform.Attachment
-import io.sentry.kotlin.multiplatform.Context
import io.sentry.kotlin.multiplatform.HttpStatusCodeRange
import io.sentry.kotlin.multiplatform.OptionsConfiguration
import io.sentry.kotlin.multiplatform.Sentry
@@ -24,15 +23,6 @@ fun configureSentryScope() {
* Initializes Sentry with given options.
* Make sure to hook this into your native platforms as early as possible
*/
-fun initializeSentry(context: Context) {
- Sentry.init(context, optionsConfiguration())
-}
-
-/**
- * Convenience initializer for Cocoa targets.
- * Kotlin -> ObjC doesn't support default parameters (yet).
- * Otherwise, you would need to do this: AppSetupKt.initializeSentry(context: nil) in Swift.
- */
fun initializeSentry() {
Sentry.init(optionsConfiguration())
}