Skip to content

Commit 7db2950

Browse files
Remove need for context in Sentry.init for Android (#117)
* Deprecate context in Sentry.init in favor of using init without it * Update comments * Update AndroidManifest.xml * Update comments * Update changelog * Update comments * Improvements * Format code * Update comment --------- Co-authored-by: Sentry Github Bot <[email protected]>
1 parent 1f39675 commit 7db2950

File tree

17 files changed

+158
-51
lines changed

17 files changed

+158
-51
lines changed

CHANGELOG.md

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

3+
## Unreleased
4+
5+
- Remove need for context in Sentry.init for Android ([#117](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/117))
6+
37
## 0.2.1
48

59
### Fixes

buildSrc/src/main/java/Config.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ object Config {
5353

5454
val roboelectric = "org.robolectric:robolectric:4.9"
5555
val junitKtx = "androidx.test.ext:junit-ktx:1.1.5"
56+
val mockitoCore = "org.mockito:mockito-core:5.4.0"
5657
}
5758

5859
object Android {

sentry-kotlin-multiplatform/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ kotlin {
6767
dependencies {
6868
implementation(Config.TestLibs.roboelectric)
6969
implementation(Config.TestLibs.junitKtx)
70+
implementation(Config.TestLibs.mockitoCore)
7071
}
7172
}
7273
val jvmMain by getting

sentry-kotlin-multiplatform/src/androidMain/AndroidManifest.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,14 @@
88
android:name="io.sentry.android.core.SentryInitProvider"
99
android:authorities="${applicationId}.SentryInitProvider"
1010
android:exported="false"
11-
tools:node="remove"/>
11+
tools:node="remove">
12+
</provider>
13+
<provider
14+
android:name="io.sentry.kotlin.multiplatform.SentryContextProvider"
15+
android:authorities="${applicationId}.SentryContextProvider"
16+
android:exported="false"
17+
tools:node="merge">
18+
</provider>
1219
</application>
1320

1421
</manifest>
Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,72 @@
11
package io.sentry.kotlin.multiplatform
22

3+
import android.content.ContentProvider
4+
import android.content.ContentValues
35
import android.content.Context
6+
import android.database.Cursor
7+
import android.net.Uri
48
import io.sentry.android.core.SentryAndroid
59
import io.sentry.kotlin.multiplatform.extensions.toAndroidSentryOptionsCallback
610

7-
internal actual fun initSentry(context: Context?, configuration: OptionsConfiguration) {
11+
internal actual fun initSentry(configuration: OptionsConfiguration) {
812
val options = SentryOptions()
913
configuration.invoke(options)
10-
context?.let {
11-
SentryAndroid.init(it, options.toAndroidSentryOptionsCallback())
12-
}
14+
SentryAndroid.init(applicationContext, options.toAndroidSentryOptionsCallback())
1315
}
1416

17+
internal lateinit var applicationContext: Context
18+
private set
19+
1520
public actual typealias Context = Context
21+
22+
/**
23+
* A ContentProvider that does NOT store or provide any data for read or write operations.
24+
*
25+
* It's only purpose is to retrieve and store the application context in an internal top-level
26+
* variable [applicationContext]. The context is used for [SentryAndroid.init].
27+
*
28+
* This does not allow for overriding the abstract query, insert, update, and delete operations
29+
* of the [ContentProvider].
30+
*/
31+
internal class SentryContextProvider : ContentProvider() {
32+
override fun onCreate(): Boolean {
33+
val context = context
34+
if (context != null) {
35+
applicationContext = context.applicationContext
36+
} else {
37+
error("Context cannot be null")
38+
}
39+
return true
40+
}
41+
42+
override fun query(
43+
uri: Uri,
44+
projection: Array<out String>?,
45+
selection: String?,
46+
selectionArgs: Array<out String>?,
47+
sortOrder: String?
48+
): Cursor? {
49+
error("Not allowed.")
50+
}
51+
52+
override fun getType(uri: Uri): String? {
53+
error("Not allowed.")
54+
}
55+
56+
override fun insert(uri: Uri, values: ContentValues?): Uri? {
57+
error("Not allowed.")
58+
}
59+
60+
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
61+
error("Not allowed.")
62+
}
63+
64+
override fun update(
65+
uri: Uri,
66+
values: ContentValues?,
67+
selection: String?,
68+
selectionArgs: Array<out String>?
69+
): Int {
70+
error("Not allowed.")
71+
}
72+
}
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
package io.sentry.kotlin.multiplatform
22

33
import androidx.test.ext.junit.runners.AndroidJUnit4
4-
import androidx.test.platform.app.InstrumentationRegistry
54
import org.junit.runner.RunWith
5+
import org.robolectric.Robolectric
6+
import kotlin.test.BeforeTest
67

78
@RunWith(AndroidJUnit4::class)
89
actual abstract class BaseSentryTest {
910
actual val platform: String = "Android"
1011
actual val authToken: String? = System.getenv("SENTRY_AUTH_TOKEN")
1112
actual fun sentryInit(optionsConfiguration: OptionsConfiguration) {
12-
val context = InstrumentationRegistry.getInstrumentation().targetContext
13-
Sentry.init(context, optionsConfiguration)
13+
Sentry.init(optionsConfiguration)
14+
}
15+
16+
@BeforeTest
17+
open fun setUp() {
18+
// Set up the provider needed for Sentry.init on Android
19+
val provider = Robolectric.buildContentProvider(SentryContextProvider::class.java)
20+
provider.create()
1421
}
1522
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package io.sentry.kotlin.multiplatform
2+
3+
import android.content.ContentValues
4+
import android.net.Uri
5+
import androidx.test.ext.junit.runners.AndroidJUnit4
6+
import org.junit.Before
7+
import org.junit.Test
8+
import org.junit.runner.RunWith
9+
import org.mockito.Mockito.mock
10+
11+
@RunWith(AndroidJUnit4::class)
12+
class SentryContextProviderTest : BaseSentryTest() {
13+
private lateinit var provider: SentryContextProvider
14+
15+
@Before
16+
override fun setUp() {
17+
provider = SentryContextProvider()
18+
}
19+
20+
// We create a nested class so this test is executed with the BeforeEach method that initializes
21+
// the actual content provider and not just a mock.
22+
class SentryContextOnCreateTest : BaseSentryTest() {
23+
@Test
24+
fun `onCreate initializes applicationContext`() {
25+
// Simple call to the lateinit applicationContext to make sure it's initialized
26+
applicationContext
27+
}
28+
}
29+
30+
fun `create does not throw Exception`() {
31+
provider.onCreate()
32+
}
33+
34+
@Test(expected = IllegalStateException::class)
35+
fun `insert throws Exception`() {
36+
val uri = mock(Uri::class.java)
37+
val values = ContentValues()
38+
provider.insert(uri, values)
39+
}
40+
41+
@Test(expected = IllegalStateException::class)
42+
fun `update throws Exception`() {
43+
val uri = mock(Uri::class.java)
44+
val values = ContentValues()
45+
provider.update(uri, values, null, null)
46+
}
47+
48+
@Test(expected = IllegalStateException::class)
49+
fun `delete throws Exception`() {
50+
val uri = mock(Uri::class.java)
51+
provider.delete(uri, null, null)
52+
}
53+
54+
@Test(expected = IllegalStateException::class)
55+
fun `getType throws Exception`() {
56+
val uri = mock(Uri::class.java)
57+
provider.getType(uri)
58+
}
59+
}

sentry-kotlin-multiplatform/src/commonJvmMain/kotlin/io/sentry/kotlin/multiplatform/SentryBridge.jvm.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ import io.sentry.kotlin.multiplatform.protocol.SentryId
99
import io.sentry.kotlin.multiplatform.protocol.User
1010
import io.sentry.kotlin.multiplatform.protocol.UserFeedback
1111

12-
internal expect fun initSentry(context: Context? = null, configuration: OptionsConfiguration)
12+
internal expect fun initSentry(configuration: OptionsConfiguration)
1313

1414
internal actual object SentryBridge {
1515

1616
actual fun init(context: Context, configuration: OptionsConfiguration) {
17-
initSentry(context, configuration)
17+
initSentry(configuration)
1818
}
1919

2020
actual fun init(configuration: OptionsConfiguration) {

sentry-kotlin-multiplatform/src/commonMain/kotlin/io/sentry/kotlin/multiplatform/SentryKMP.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public typealias ScopeCallback = (Scope) -> Unit
1111
public typealias OptionsConfiguration = (SentryOptions) -> Unit
1212

1313
/** The context used for Android initialization. */
14+
@Deprecated("No longer necessary to initialize Sentry on Android.")
1415
public expect abstract class Context
1516

1617
/** Sentry Kotlin Multiplatform SDK API entry point. */
@@ -24,6 +25,10 @@ public object Sentry {
2425
*/
2526
@OptIn(ExperimentalObjCRefinement::class)
2627
@HiddenFromObjC
28+
@Deprecated(
29+
"Use init(OptionsConfiguration) instead.",
30+
ReplaceWith("Sentry.init(configuration)")
31+
)
2732
public fun init(context: Context, configuration: OptionsConfiguration) {
2833
SentryBridge.init(context, configuration)
2934
}

sentry-kotlin-multiplatform/src/jvmMain/kotlin/io/sentry/kotlin/multiplatform/SentryInit.jvm.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package io.sentry.kotlin.multiplatform
33
import io.sentry.Sentry
44
import io.sentry.kotlin.multiplatform.extensions.toJvmSentryOptionsCallback
55

6-
internal actual fun initSentry(context: Context?, configuration: OptionsConfiguration) {
6+
internal actual fun initSentry(configuration: OptionsConfiguration) {
77
val options = SentryOptions()
88
configuration.invoke(options)
99
Sentry.init(options.toJvmSentryOptionsCallback())

0 commit comments

Comments
 (0)