Skip to content

Commit f4206aa

Browse files
committed
Merge branch 'main' into markushi/feat/app-start-info
2 parents 26e098a + 0eaac1e commit f4206aa

File tree

41 files changed

+1484
-811
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1484
-811
lines changed

CHANGELOG.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,26 @@
44

55
### Features
66

7+
- Add `installGroupsOverride` parameter and `installGroups` property to Build Distribution SDK ([#5062](https://github.com/getsentry/sentry-java/pull/5062))
8+
- Update Android targetSdk to API 36 (Android 16) ([#5016](https://github.com/getsentry/sentry-java/pull/5016))
9+
- Add AndroidManifest support for Spotlight configuration via `io.sentry.spotlight.enable` and `io.sentry.spotlight.url` ([#5064](https://github.com/getsentry/sentry-java/pull/5064))
710
- Add ApplicationStartInfo API support for Android 15+ ([#5055](https://github.com/getsentry/sentry-java/pull/5055))
8-
- Captures detailed app startup timing data from Android system
9-
- Creates transactions with milestone spans (bind_application, application_oncreate, ttid, ttfd)
10-
- Enriches with AppStartMetrics data (content provider spans, class names)
11+
- Captures detailed app startup timing data based on [ApplicationStartInfo APIs](https://developer.android.com/reference/android/app/ApplicationStartInfo)
1112
- Opt-in via `SentryAndroidOptions.setEnableApplicationStartInfo(boolean)` (disabled by default)
12-
- Update Android targetSdk to API 36 (Android 16) ([#5016](https://github.com/getsentry/sentry-java/pull/5016))
13+
14+
### Fixes
15+
16+
- Extract `SpotlightIntegration` to separate `sentry-spotlight` module to prevent insecure HTTP URLs from appearing in release APKs ([#5064](https://github.com/getsentry/sentry-java/pull/5064))
17+
- **Breaking:** Users who enable Spotlight must now add the `io.sentry:sentry-spotlight` dependency:
18+
```kotlin
19+
dependencies {
20+
debugImplementation("io.sentry:sentry-spotlight:<version>")
21+
}
22+
```
23+
24+
### Fixes
25+
26+
- Fix scroll target detection for Jetpack Compose ([#5017](https://github.com/getsentry/sentry-java/pull/5017))
1327

1428
### Internal
1529

gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
99

1010
# AndroidX required by AGP >= 3.6.x
1111
android.useAndroidX=true
12+
android.experimental.lint.version=8.9.0
1213

1314
# Release information
1415
versionName=8.31.0

gradle/libs.versions.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ androidx-annotation = { module = "androidx.annotation:annotation", version = "1.
8282
androidx-activity-compose = { module = "androidx.activity:activity-compose", version = "1.8.2" }
8383
androidx-compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "androidxCompose" }
8484
androidx-compose-foundation-layout = { module = "androidx.compose.foundation:foundation-layout", version.ref = "androidxCompose" }
85-
androidx-compose-material3 = { module = "androidx.compose.material3:material3", version = "1.2.1" }
85+
androidx-compose-material3 = { module = "androidx.compose.material3:material3", version = "1.4.0" }
86+
androidx-compose-material-icons-core = { module = "androidx.compose.material:material-icons-core", version="1.7.8" }
87+
androidx-compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version="1.7.8" }
8688
androidx-compose-ui = { module = "androidx.compose.ui:ui", version.ref = "androidxCompose" }
8789
# Note: don't change without testing forwards compatibility
8890
androidx-compose-ui-replay = { module = "androidx.compose.ui:ui", version = "1.5.0" }

sentry-android-core/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ dependencies {
102102
testImplementation(libs.mockito.kotlin)
103103
testImplementation(libs.mockito.inline)
104104
testImplementation(projects.sentryTestSupport)
105+
testImplementation(projects.sentrySpotlight)
105106
testImplementation(projects.sentryAndroidFragment)
106107
testImplementation(projects.sentryAndroidTimber)
107108
testImplementation(projects.sentryAndroidReplay)

sentry-android-core/proguard-rules.pro

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,8 @@
8383
-dontwarn io.sentry.android.distribution.DistributionIntegration
8484
-keepnames class io.sentry.android.distribution.DistributionIntegration
8585
##---------------End: proguard configuration for sentry-android-distribution ----------
86+
87+
##---------------Begin: proguard configuration for sentry-spotlight ----------
88+
-dontwarn io.sentry.spotlight.SpotlightIntegration
89+
-keepnames class io.sentry.spotlight.SpotlightIntegration
90+
##---------------End: proguard configuration for sentry-spotlight ----------

sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ final class ManifestMetadataReader {
164164

165165
static final String FEEDBACK_SHOW_BRANDING = "io.sentry.feedback.show-branding";
166166

167+
static final String SPOTLIGHT_ENABLE = "io.sentry.spotlight.enable";
168+
169+
static final String SPOTLIGHT_CONNECTION_URL = "io.sentry.spotlight.url";
170+
167171
/** ManifestMetadataReader ctor */
168172
private ManifestMetadataReader() {}
169173

@@ -642,6 +646,15 @@ static void applyMetadata(
642646
metadata, logger, FEEDBACK_USE_SENTRY_USER, feedbackOptions.isUseSentryUser()));
643647
feedbackOptions.setShowBranding(
644648
readBool(metadata, logger, FEEDBACK_SHOW_BRANDING, feedbackOptions.isShowBranding()));
649+
650+
options.setEnableSpotlight(
651+
readBool(metadata, logger, SPOTLIGHT_ENABLE, options.isEnableSpotlight()));
652+
653+
final @Nullable String spotlightUrl =
654+
readString(metadata, logger, SPOTLIGHT_CONNECTION_URL, null);
655+
if (spotlightUrl != null) {
656+
options.setSpotlightConnectionUrl(spotlightUrl);
657+
}
645658
}
646659
options
647660
.getLogger()

sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2217,4 +2217,76 @@ class ManifestMetadataReaderTest {
22172217
assertTrue(headers.contains("Authorization"))
22182218
assertTrue(headers.contains("X-Custom-Header"))
22192219
}
2220+
2221+
// Spotlight Configuration Tests
2222+
2223+
@Test
2224+
fun `applyMetadata reads spotlight enabled and keeps default value if not found`() {
2225+
// Arrange
2226+
val context = fixture.getContext()
2227+
2228+
// Act
2229+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
2230+
2231+
// Assert
2232+
assertFalse(fixture.options.isEnableSpotlight)
2233+
}
2234+
2235+
@Test
2236+
fun `applyMetadata reads spotlight enabled to options`() {
2237+
// Arrange
2238+
val bundle = bundleOf(ManifestMetadataReader.SPOTLIGHT_ENABLE to true)
2239+
val context = fixture.getContext(metaData = bundle)
2240+
2241+
// Act
2242+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
2243+
2244+
// Assert
2245+
assertTrue(fixture.options.isEnableSpotlight)
2246+
}
2247+
2248+
@Test
2249+
fun `applyMetadata reads spotlight url and keeps null if not found`() {
2250+
// Arrange
2251+
val context = fixture.getContext()
2252+
2253+
// Act
2254+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
2255+
2256+
// Assert
2257+
assertNull(fixture.options.spotlightConnectionUrl)
2258+
}
2259+
2260+
@Test
2261+
fun `applyMetadata reads spotlight url to options`() {
2262+
// Arrange
2263+
val expectedUrl = "http://10.0.2.2:8969/stream"
2264+
val bundle = bundleOf(ManifestMetadataReader.SPOTLIGHT_CONNECTION_URL to expectedUrl)
2265+
val context = fixture.getContext(metaData = bundle)
2266+
2267+
// Act
2268+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
2269+
2270+
// Assert
2271+
assertEquals(expectedUrl, fixture.options.spotlightConnectionUrl)
2272+
}
2273+
2274+
@Test
2275+
fun `applyMetadata reads both spotlight enabled and url to options`() {
2276+
// Arrange
2277+
val expectedUrl = "http://localhost:8969/stream"
2278+
val bundle =
2279+
bundleOf(
2280+
ManifestMetadataReader.SPOTLIGHT_ENABLE to true,
2281+
ManifestMetadataReader.SPOTLIGHT_CONNECTION_URL to expectedUrl,
2282+
)
2283+
val context = fixture.getContext(metaData = bundle)
2284+
2285+
// Act
2286+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
2287+
2288+
// Assert
2289+
assertTrue(fixture.options.isEnableSpotlight)
2290+
assertEquals(expectedUrl, fixture.options.spotlightConnectionUrl)
2291+
}
22202292
}

sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import io.sentry.SentryOptions
2626
import io.sentry.SentryOptions.BeforeSendCallback
2727
import io.sentry.Session
2828
import io.sentry.ShutdownHookIntegration
29-
import io.sentry.SpotlightIntegration
3029
import io.sentry.SystemOutLogger
3130
import io.sentry.UncaughtExceptionHandlerIntegration
3231
import io.sentry.android.core.cache.AndroidEnvelopeCache
@@ -46,6 +45,7 @@ import io.sentry.cache.PersistingScopeObserver.TRANSACTION_FILENAME
4645
import io.sentry.cache.tape.QueueFile
4746
import io.sentry.protocol.Contexts
4847
import io.sentry.protocol.SentryId
48+
import io.sentry.spotlight.SpotlightIntegration
4949
import io.sentry.test.applyTestOptions
5050
import io.sentry.transport.NoOpEnvelopeCache
5151
import io.sentry.util.StringUtils

sentry-android-distribution/src/main/java/io/sentry/android/distribution/DistributionHttpClient.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ internal class DistributionHttpClient(private val options: SentryOptions) {
2727
val versionCode: Long,
2828
val versionName: String,
2929
val buildConfiguration: String,
30+
val installGroupsOverride: List<String>? = null,
3031
)
3132

3233
/**
@@ -58,6 +59,9 @@ internal class DistributionHttpClient(private val options: SentryOptions) {
5859
append("&build_number=${URLEncoder.encode(params.versionCode.toString(), "UTF-8")}")
5960
append("&build_version=${URLEncoder.encode(params.versionName, "UTF-8")}")
6061
append("&build_configuration=${URLEncoder.encode(params.buildConfiguration, "UTF-8")}")
62+
params.installGroupsOverride?.forEach { group ->
63+
append("&install_groups=${URLEncoder.encode(group, "UTF-8")}")
64+
}
6165
}
6266
val url = URL(urlString)
6367

sentry-android-distribution/src/main/java/io/sentry/android/distribution/UpdateResponseParser.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ internal class UpdateResponseParser(private val options: SentryOptions) {
5757
val downloadUrl = json.optString("download_url", "")
5858
val appName = json.optString("app_name", "")
5959
val createdDate = json.optString("created_date", "")
60+
val installGroups = parseInstallGroups(json)
6061

6162
// Validate required fields (optString returns "null" for null values)
6263
val missingFields = mutableListOf<String>()
@@ -77,6 +78,26 @@ internal class UpdateResponseParser(private val options: SentryOptions) {
7778
)
7879
}
7980

80-
return UpdateInfo(id, buildVersion, buildNumber, downloadUrl, appName, createdDate)
81+
return UpdateInfo(
82+
id,
83+
buildVersion,
84+
buildNumber,
85+
downloadUrl,
86+
appName,
87+
createdDate,
88+
installGroups,
89+
)
90+
}
91+
92+
private fun parseInstallGroups(json: JSONObject): List<String>? {
93+
val installGroupsArray = json.optJSONArray("install_groups") ?: return null
94+
val installGroups = mutableListOf<String>()
95+
for (i in 0 until installGroupsArray.length()) {
96+
val group = installGroupsArray.optString(i)
97+
if (group.isNotEmpty() && group != "null") {
98+
installGroups.add(group)
99+
}
100+
}
101+
return if (installGroups.isEmpty()) null else installGroups
81102
}
82103
}

0 commit comments

Comments
 (0)