From c0ed4244162d9168158b06f223a635d3f150c6a1 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Fri, 30 Oct 2026 12:58:55 +0100 Subject: [PATCH 1/3] feat(android): [minSdk 26 1] Raise minSdk from 21 to 26 Raise the minimum supported Android API level of all Android modules to 26. The sample and the sqlite tests no longer need to override the floor for androidx.sqlite. Robolectric cannot load an APK below its minSdk, so core tests now default to API 26. Tests that only covered pre-26 code paths are removed, and tests whose behavior differs on API 26 are adjusted. ObsoleteSdkInt is disabled until the next change removes the checks this makes obsolete. Refs JAVA-547 Co-Authored-By: Claude Opus 5.5 --- README.md | 20 ++++++------- gradle/libs.versions.toml | 2 +- sentry-android-core/build.gradle.kts | 2 ++ .../core/ActivityLifecycleIntegrationTest.kt | 27 ----------------- .../core/AndroidOptionsInitializerTest.kt | 30 ------------------- .../android/core/AppStartExtensionTest.kt | 2 +- .../core/ScreenshotEventProcessorTest.kt | 2 +- .../sentry/android/core/SentryAndroidTest.kt | 3 +- .../core/SentryPerformanceProviderTest.kt | 2 +- .../core/SentryUserFeedbackFormTest.kt | 2 +- .../SentryGestureListenerPeekDecorViewTest.kt | 16 +++++----- .../core/performance/AppStartMetricsTest.kt | 2 +- .../src/test/resources/robolectric.properties | 2 +- sentry-android-ndk/build.gradle.kts | 2 ++ sentry-android-replay/build.gradle.kts | 2 ++ .../android/replay/ReplayIntegrationTest.kt | 10 ------- .../src/test/AndroidManifest.xml | 13 -------- .../sentry-samples-android/build.gradle.kts | 3 +- 18 files changed, 33 insertions(+), 109 deletions(-) delete mode 100644 sentry-android-sqlite/src/test/AndroidManifest.xml diff --git a/README.md b/README.md index a073fcad6c1..b42bd8a29e4 100644 --- a/README.md +++ b/README.md @@ -18,17 +18,17 @@ Sentry SDK for Java and Android | Packages | Maven Central | Minimum Android API Version | |-----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| ------- | -| sentry-android | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android) | 21 | -| sentry-android-core | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-core?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-core) | 21 | -| sentry-android-distribution | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-distribution?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-distribution) | 21 | -| sentry-android-ndk | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-ndk?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-ndk) | 21 | -| sentry-android-timber | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-timber?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-timber) | 21 | -| sentry-android-fragment | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-fragment?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-fragment) | 21 | -| sentry-android-navigation | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-navigation?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-navigation) | 21 | -| sentry-android-navigation3 | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-navigation3?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-navigation3) | 23 | -| sentry-android-sqlite | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-sqlite?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-sqlite) | 21 | +| sentry-android | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android) | 26 | +| sentry-android-core | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-core?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-core) | 26 | +| sentry-android-distribution | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-distribution?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-distribution) | 26 | +| sentry-android-ndk | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-ndk?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-ndk) | 26 | +| sentry-android-timber | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-timber?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-timber) | 26 | +| sentry-android-fragment | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-fragment?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-fragment) | 26 | +| sentry-android-navigation | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-navigation?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-navigation) | 26 | +| sentry-android-navigation3 | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-navigation3?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-navigation3) | 26 | +| sentry-android-sqlite | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-sqlite?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-sqlite) | 26 | | sentry-android-replay | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-replay?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-replay) | 26 | -| sentry-compose-android | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-compose-android?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-compose-android) | 21 | +| sentry-compose-android | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-compose-android?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-compose-android) | 26 | | sentry-compose-desktop | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-compose-desktop?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-compose-desktop) | | sentry-compose | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-compose?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-compose) | | sentry | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry) | 21 | diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7cbe8e626c8..d00cb323caf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -56,7 +56,7 @@ sqldelight = "2.3.2" targetSdk = "37" compileSdk = "37" compileSdkMinor = "2" -minSdk = "21" +minSdk = "26" [plugins] kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } diff --git a/sentry-android-core/build.gradle.kts b/sentry-android-core/build.gradle.kts index 0f791a46019..511fd41d4c0 100644 --- a/sentry-android-core/build.gradle.kts +++ b/sentry-android-core/build.gradle.kts @@ -55,6 +55,8 @@ android { lint { warningsAsErrors = true checkDependencies = true + // TODO(JAVA-547): Re-enable once the SDK_INT checks made obsolete by minSdk 26 are removed. + disable += "ObsoleteSdkInt" // We run a full lint analysis as build part in CI, so skip vital checks for assemble tasks. checkReleaseBuilds = false diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt index 27f339aff31..326753a1283 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt @@ -703,33 +703,6 @@ class ActivityLifecycleIntegrationTest { assertEquals(SpanStatus.OK, transaction.status) } - @Test - @Config(sdk = [Build.VERSION_CODES.M]) - fun `onHeadlessAppStart creates standalone App Start transaction on API 23`() { - val sut = fixture.getSut { - it.tracesSampleRate = 1.0 - it.isEnableStandaloneAppStartTracing = true - } - sut.register(fixture.scopes, fixture.options) - prepareHeadlessSdkInitAppStart() - - driveHeadlessAppStart() - - assertEquals(1, fixture.capturedContexts.size) - val context = fixture.capturedContexts.single() - val options = fixture.capturedOptions.single() - val transaction = fixture.createdTransactions.single() - assertEquals(ActivityLifecycleIntegration.STANDALONE_APP_START_OP, context.operation) - assertEquals("App Start", context.name) - assertEquals(DateUtils.millisToNanos(100), options.startTimestamp!!.nanoTimestamp()) - assertEquals( - transaction.spanContext.traceId, - AppStartMetrics.getInstance().getAppStartTraceId(), - ) - assertTrue(transaction.isFinished) - assertEquals(SpanStatus.OK, transaction.status) - } - @Test fun `onHeadlessAppStart creates standalone App Start transaction when appStartType is WARM`() { val sut = fixture.getSut { diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt index b3cba238561..345cefcc9bd 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt @@ -370,22 +370,6 @@ class AndroidOptionsInitializerTest { assertTrue(fixture.sentryOptions.transportGate is AndroidTransportGate) } - @Config(sdk = [Build.VERSION_CODES.N_MR1]) - @Test - fun `init on API 25 and lower sets SSLSocketFactory with bundled Sentry root CAs`() { - fixture.initSut(configureOptions = { dsn = SAAS_DSN }) - - assertIs(fixture.sentryOptions.sslSocketFactory) - } - - @Config(sdk = [Build.VERSION_CODES.N_MR1]) - @Test - fun `init on API 25 and lower does not set SSLSocketFactory for self-hosted DSN`() { - fixture.initSut(configureOptions = { dsn = "https://key@sentry.example.com/1" }) - - assertNull(fixture.sentryOptions.sslSocketFactory) - } - @Config(sdk = [Build.VERSION_CODES.O]) @Test fun `init on API 26 and higher does not set SSLSocketFactory`() { @@ -394,20 +378,6 @@ class AndroidOptionsInitializerTest { assertNull(fixture.sentryOptions.sslSocketFactory) } - @Config(sdk = [Build.VERSION_CODES.N_MR1]) - @Test - fun `init on API 25 and lower keeps user provided SSLSocketFactory`() { - val sslSocketFactory = mock() - fixture.initSut( - configureOptions = { - dsn = SAAS_DSN - this.sslSocketFactory = sslSocketFactory - } - ) - - assertEquals(sslSocketFactory, fixture.sentryOptions.sslSocketFactory) - } - @Test fun `init should set Android continuous profiler`() { fixture.initSut() diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AppStartExtensionTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AppStartExtensionTest.kt index 38f7f29e7df..4439246ef33 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AppStartExtensionTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AppStartExtensionTest.kt @@ -28,7 +28,7 @@ import org.mockito.kotlin.whenever import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) -@Config(sdk = [Build.VERSION_CODES.N]) +@Config(sdk = [Build.VERSION_CODES.O]) class AppStartExtensionTest { private val metrics = mock() diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt index 0abb232b807..2c2862ed685 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt @@ -202,7 +202,7 @@ class ScreenshotEventProcessorTest { } @Test - @Config(sdk = [23]) + @Config(sdk = [26]) fun `when screenshot event processor is called from background thread it executes on main thread`() { val sut = fixture.getSut(true) whenever(fixture.threadChecker.isMainThread).thenReturn(false) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt index c94fd5890ca..001fe09294d 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt @@ -88,7 +88,7 @@ import org.robolectric.shadows.ShadowActivityManager.ApplicationExitInfoBuilder import timber.log.Timber @RunWith(AndroidJUnit4::class) -@Config(sdk = [Build.VERSION_CODES.N], shadows = [SentryShadowProcess::class]) +@Config(sdk = [Build.VERSION_CODES.O], shadows = [SentryShadowProcess::class]) class SentryAndroidTest { @get:Rule val tmpDir = TemporaryFolder() @@ -178,6 +178,7 @@ class SentryAndroidTest { fun `set up`() { Sentry.close() AppStartMetrics.getInstance().clear() + ContextUtils.resetInstance() context = ApplicationProvider.getApplicationContext() val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager? fixture.shadowActivityManager = Shadow.extract(activityManager) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryPerformanceProviderTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryPerformanceProviderTest.kt index bff6cdfad37..a4cbce163aa 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryPerformanceProviderTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryPerformanceProviderTest.kt @@ -33,7 +33,7 @@ import org.mockito.kotlin.whenever import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) -@Config(sdk = [Build.VERSION_CODES.N], shadows = [SentryShadowProcess::class]) +@Config(sdk = [Build.VERSION_CODES.O], shadows = [SentryShadowProcess::class]) class SentryPerformanceProviderTest { private lateinit var cache: File private lateinit var sentryCache: File diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryUserFeedbackFormTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryUserFeedbackFormTest.kt index 840a0772a7f..22a35016e77 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryUserFeedbackFormTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryUserFeedbackFormTest.kt @@ -268,7 +268,7 @@ class SentryUserFeedbackFormTest { val context = ContextThemeWrapper(fixture.application, android.R.style.Theme_Material_Light) assertThat(SentryUserFeedbackForm.resolveTheme(context, 0)) - .isEqualTo(android.R.style.Theme_Material_Light_Dialog_Alert) + .isEqualTo(android.R.style.ThemeOverlay_Material_Dialog_Alert) } @Test diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerPeekDecorViewTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerPeekDecorViewTest.kt index 7b26aa55c79..5d6fd79f178 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerPeekDecorViewTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerPeekDecorViewTest.kt @@ -2,31 +2,29 @@ package io.sentry.android.core.internal.gestures import android.app.Activity import android.view.MotionEvent +import android.view.Window import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.Breadcrumb import io.sentry.IScopes import io.sentry.android.core.SentryAndroidOptions import io.sentry.util.LazyEvaluator import kotlin.test.Test -import kotlin.test.assertNull import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.verify -import org.robolectric.Robolectric.buildActivity @RunWith(AndroidJUnit4::class) class SentryGestureListenerPeekDecorViewTest { @Test fun `does not force decor view creation when peekDecorView returns null`() { - // A plain Activity that never calls setContentView — peekDecorView() should return null - val activity = buildActivity(Activity::class.java).create().get() - - // Sanity check: decor view has not been created yet - assertNull(activity.window.peekDecorView()) + // Robolectric creates the decor view in onCreate on API 23+, so mock a window without one + val window = mock() + val activity = mock { on { this.window } doReturn window } val scopes = mock() val options = @@ -39,8 +37,8 @@ class SentryGestureListenerPeekDecorViewTest { val sut = SentryGestureListener(activity, scopes, options) sut.onSingleTapUp(mock()) - // The key assertion: peekDecorView is still null — we did not force view hierarchy creation - assertNull(activity.window.peekDecorView()) + // The key assertion: we did not force view hierarchy creation + verify(window, never()).decorView // And no breadcrumb was captured verify(scopes, never()).addBreadcrumb(any(), anyOrNull()) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/performance/AppStartMetricsTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/performance/AppStartMetricsTest.kt index a036583adb8..51ae69aabd5 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/performance/AppStartMetricsTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/performance/AppStartMetricsTest.kt @@ -43,7 +43,7 @@ import org.robolectric.Shadows import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) -@Config(sdk = [Build.VERSION_CODES.N], shadows = [SentryShadowProcess::class]) +@Config(sdk = [Build.VERSION_CODES.O], shadows = [SentryShadowProcess::class]) class AppStartMetricsTest { @Before fun setup() { diff --git a/sentry-android-core/src/test/resources/robolectric.properties b/sentry-android-core/src/test/resources/robolectric.properties index c216eeab9dc..b2706226677 100644 --- a/sentry-android-core/src/test/resources/robolectric.properties +++ b/sentry-android-core/src/test/resources/robolectric.properties @@ -1,2 +1,2 @@ -sdk=21 +sdk=26 application=io.sentry.android.core.ApplicationStub diff --git a/sentry-android-ndk/build.gradle.kts b/sentry-android-ndk/build.gradle.kts index dff85906ca1..8b176ccd3be 100644 --- a/sentry-android-ndk/build.gradle.kts +++ b/sentry-android-ndk/build.gradle.kts @@ -44,6 +44,8 @@ android { lint { warningsAsErrors = true checkDependencies = true + // TODO(JAVA-547): Re-enable once the SDK_INT checks made obsolete by minSdk 26 are removed. + disable += "ObsoleteSdkInt" // We run a full lint analysis as build part in CI, so skip vital checks for assemble tasks. checkReleaseBuilds = false diff --git a/sentry-android-replay/build.gradle.kts b/sentry-android-replay/build.gradle.kts index 1a92f9a0b36..1132a966d74 100644 --- a/sentry-android-replay/build.gradle.kts +++ b/sentry-android-replay/build.gradle.kts @@ -55,6 +55,8 @@ android { lint { warningsAsErrors = true checkDependencies = true + // TODO(JAVA-547): Re-enable once the SDK_INT checks made obsolete by minSdk 26 are removed. + disable += "ObsoleteSdkInt" // We run a full lint analysis as build part in CI, so skip vital checks for assemble tasks. checkReleaseBuilds = false diff --git a/sentry-android-replay/src/test/java/io/sentry/android/replay/ReplayIntegrationTest.kt b/sentry-android-replay/src/test/java/io/sentry/android/replay/ReplayIntegrationTest.kt index 70e8426d124..210587ea49a 100644 --- a/sentry-android-replay/src/test/java/io/sentry/android/replay/ReplayIntegrationTest.kt +++ b/sentry-android-replay/src/test/java/io/sentry/android/replay/ReplayIntegrationTest.kt @@ -183,16 +183,6 @@ class ReplayIntegrationTest { SentryIntegrationPackageStorage.getInstance().clearStorage() } - @Test - @Config(sdk = [24]) - fun `when API is below 26, does not register`() { - val replay = fixture.getSut(context) - - replay.register(fixture.scopes, fixture.options) - - assertFalse(replay.isEnabled.get()) - } - @Test fun `when no sample rate is set, still registers`() { val replay = fixture.getSut(context, 0.0, 0.0) diff --git a/sentry-android-sqlite/src/test/AndroidManifest.xml b/sentry-android-sqlite/src/test/AndroidManifest.xml deleted file mode 100644 index 967265a1f16..00000000000 --- a/sentry-android-sqlite/src/test/AndroidManifest.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/sentry-samples/sentry-samples-android/build.gradle.kts b/sentry-samples/sentry-samples-android/build.gradle.kts index a9d4ea8ac5f..de8ca665cc2 100644 --- a/sentry-samples/sentry-samples-android/build.gradle.kts +++ b/sentry-samples/sentry-samples-android/build.gradle.kts @@ -48,8 +48,7 @@ android { defaultConfig { applicationId = "io.sentry.samples.android" - // androidx.sqlite 2.6+ require minSdk 23; the Sentry SDK still supports 21. - minSdk = 23 + minSdk = libs.versions.minSdk.get().toInt() targetSdk = libs.versions.targetSdk.get().toInt() versionCode = 2 versionName = project.version.toString() From 447ce02d476c2e3b13b14fd0fd211ced87fe350c Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Fri, 30 Oct 2026 13:05:02 +0100 Subject: [PATCH 2/3] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1185d47aaf9..2d46832c9f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ - Decide whether foregrounding the app starts a new session on a monotonic clock instead of the wall clock, so that a device time change no longer starts a session that should have been resumed, or resumes one that should have ended. This affects session counts ([#6096](https://github.com/getsentry/sentry-java/pull/6096)) - `SentryUserFeedbackForm` now extends `android.app.Dialog` instead of `android.app.AlertDialog` ([#6338](https://github.com/getsentry/sentry-java/pull/6338)) - The form never used `AlertDialog` features. Code that calls `AlertDialog`-only methods on it, such as `setButton` or `setMessage`, no longer compiles. +- Raise the minimum supported Android API level (`minSdk`) of all Android modules from 21 to 26. Apps with a lower `minSdk` must raise it or stay on 8.x ([#6435](https://github.com/getsentry/sentry-java/pull/6435)) ## 8.62.0 From cc24dde1c78f81cf57385cef7e44d569a51d7d1b Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Fri, 30 Oct 2026 13:58:22 +0100 Subject: [PATCH 3/3] build: Fix UI test app lint and AGP matrix R8 crash at minSdk 26 The UI test app had two version checks that minSdk 26 makes obsolete. The R8 bundled with AGP 9.0 and 9.2 crashes with a NullPointerException in its API level database while optimizing androidx.core's LocationManagerCompat at minSdk 26. When VERSION_AGP overrides AGP, as in the AGP matrix, put the R8 from the default AGP 9.4.0 ahead of AGP on the buildscript classpath. Refs JAVA-547 Co-Authored-By: Claude Opus 5.5 --- build.gradle.kts | 2 ++ buildSrc/src/main/java/Config.kt | 5 +++++ .../sentry-uitest-android/src/main/AndroidManifest.xml | 3 +-- .../java/io/sentry/uitest/android/ProfilingSampleActivity.kt | 5 +---- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3ed35d2da82..b72f85e9500 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,6 +34,8 @@ buildscript { google() } dependencies { + // R8 must come before AGP on the classpath to replace the R8 bundled with AGP. + Config.BuildPlugins.r8?.let { classpath(it) } classpath(Config.BuildPlugins.androidGradle) // add classpath of sentry android gradle plugin diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index 05786658ef3..38182d51a12 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -7,6 +7,11 @@ object Config { object BuildPlugins { val androidGradle = "com.android.tools.build:gradle:$AGP" + + // The R8 bundled with AGP 9.0 and 9.2 crashes on androidx.core at minSdk 26, so builds that + // override AGP (the AGP matrix) run the R8 that ships with the default AGP instead. + val r8: String? + get() = if (System.getenv("VERSION_AGP") != null) "com.android.tools:r8:9.4.14" else null } object Android { diff --git a/sentry-android-integration-tests/sentry-uitest-android/src/main/AndroidManifest.xml b/sentry-android-integration-tests/sentry-uitest-android/src/main/AndroidManifest.xml index 6bcad62e6de..0b676446a85 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/src/main/AndroidManifest.xml +++ b/sentry-android-integration-tests/sentry-uitest-android/src/main/AndroidManifest.xml @@ -9,8 +9,7 @@ android:label="Sentry End2End Tests" android:largeHeap="true" android:icon="@android:mipmap/sym_def_app_icon" - android:usesCleartextTraffic="true" - tools:targetApi="m"> + android:usesCleartextTraffic="true"> diff --git a/sentry-android-integration-tests/sentry-uitest-android/src/main/java/io/sentry/uitest/android/ProfilingSampleActivity.kt b/sentry-android-integration-tests/sentry-uitest-android/src/main/java/io/sentry/uitest/android/ProfilingSampleActivity.kt index fdfeb61e05f..1ac1590e78a 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/src/main/java/io/sentry/uitest/android/ProfilingSampleActivity.kt +++ b/sentry-android-integration-tests/sentry-uitest-android/src/main/java/io/sentry/uitest/android/ProfilingSampleActivity.kt @@ -2,7 +2,6 @@ package io.sentry.uitest.android import android.graphics.Bitmap import android.graphics.Color -import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup @@ -31,9 +30,7 @@ class ProfilingSampleActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - window.setSustainedPerformanceMode(true) - } + window.setSustainedPerformanceMode(true) binding = ActivityProfilingSampleBinding.inflate(layoutInflater) setContentView(binding.root)