diff --git a/CHANGELOG.md b/CHANGELOG.md index 743f2f1776f..a745c965485 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +# Unreleased + +### Improvements + +- Update Android targetSdk to API 36 (Android 16) ([#5016](https://github.com/getsentry/sentry-java/pull/5016)) + ## 8.31.0 ### Features diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fc9d93d72ba..44a9d9bdbaa 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -34,8 +34,8 @@ springboot2 = "2.7.18" springboot3 = "3.5.0" springboot4 = "4.0.0" # Android -targetSdk = "34" -compileSdk = "34" +targetSdk = "36" +compileSdk = "36" minSdk = "21" spotless = "7.0.4" gummyBears = "0.12.0" @@ -204,15 +204,15 @@ tomcat-catalina-jakarta = { module = "org.apache.tomcat:tomcat-catalina", versio tomcat-embed-jasper-jakarta = { module = "org.apache.tomcat.embed:tomcat-embed-jasper", version = "11.0.10" } # test libraries -androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version = "1.6.8" } +androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version = "1.10.1" } androidx-test-core = { module = "androidx.test:core", version.ref = "androidxTestCore" } androidx-test-core-ktx = { module = "androidx.test:core-ktx", version.ref = "androidxTestCore" } androidx-test-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espresso" } androidx-test-espresso-idling-resource = { module = "androidx.test.espresso:espresso-idling-resource", version.ref = "espresso" } -androidx-test-ext-junit = { module = "androidx.test.ext:junit", version = "1.1.5" } -androidx-test-orchestrator = { module = "androidx.test:orchestrator", version = "1.5.0" } +androidx-test-ext-junit = { module = "androidx.test.ext:junit", version = "1.3.0" } +androidx-test-orchestrator = { module = "androidx.test:orchestrator", version = "1.6.1" } androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidxTestCore" } -androidx-test-runner = { module = "androidx.test:runner", version = "1.6.2" } +androidx-test-runner = { module = "androidx.test:runner", version = "1.7.0" } awaitility-kotlin = { module = "org.awaitility:awaitility-kotlin", version = "4.1.1" } awaitility-kotlin-spring7 = { module = "org.awaitility:awaitility-kotlin", version = "4.3.0" } awaitility3-kotlin = { module = "org.awaitility:awaitility-kotlin", version = "3.1.6" } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegration.java index 196c9f32205..43ed3422cd9 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegration.java @@ -98,6 +98,7 @@ public void onConfigurationChanged(@NotNull Configuration newConfig) { executeInBackground(() -> captureConfigurationChangedBreadcrumb(now, newConfig)); } + @SuppressWarnings("deprecation") @Override public void onLowMemory() { // we do this in onTrimMemory below already, this is legacy API (14 or below) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/AndroidThreadChecker.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/AndroidThreadChecker.java index ccd4a92b276..d43536af19b 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/AndroidThreadChecker.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/AndroidThreadChecker.java @@ -1,5 +1,6 @@ package io.sentry.android.core.internal.util; +import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Process; @@ -24,14 +25,32 @@ private AndroidThreadChecker() { new Handler(Looper.getMainLooper()).post(() -> mainThreadSystemId = Process.myTid()); } + /** + * Gets the thread ID in a way that's compatible across Android versions. + * + *

Uses {@link Thread#threadId()} on Android 14 (API 34) and above, and falls back to {@link + * Thread#getId()} on older versions. + * + * @param thread the thread to get the ID for + * @return the thread ID + */ + @SuppressWarnings("deprecation") + public static long getThreadId(final @NotNull Thread thread) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) { + return thread.threadId(); + } else { + return thread.getId(); + } + } + @Override public boolean isMainThread(final long threadId) { - return Looper.getMainLooper().getThread().getId() == threadId; + return getThreadId(Looper.getMainLooper().getThread()) == threadId; } @Override public boolean isMainThread(final @NotNull Thread thread) { - return isMainThread(thread.getId()); + return isMainThread(getThreadId(thread)); } @Override diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/performance/ActivityLifecycleSpanHelper.java b/sentry-android-core/src/main/java/io/sentry/android/core/performance/ActivityLifecycleSpanHelper.java index fead459ba88..accb56db0dc 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/performance/ActivityLifecycleSpanHelper.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/performance/ActivityLifecycleSpanHelper.java @@ -8,6 +8,7 @@ import io.sentry.SpanDataConvention; import io.sentry.SpanStatus; import io.sentry.android.core.AndroidDateUtils; +import io.sentry.android.core.internal.util.AndroidThreadChecker; import java.util.concurrent.TimeUnit; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -129,7 +130,9 @@ public void clear() { } private void setDefaultStartSpanData(final @NotNull ISpan span) { - span.setData(SpanDataConvention.THREAD_ID, Looper.getMainLooper().getThread().getId()); + span.setData( + SpanDataConvention.THREAD_ID, + AndroidThreadChecker.getThreadId(Looper.getMainLooper().getThread())); span.setData(SpanDataConvention.THREAD_NAME, "main"); span.setData(SpanDataConvention.CONTRIBUTES_TTID, true); span.setData(SpanDataConvention.CONTRIBUTES_TTFD, true); diff --git a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/UserFeedbackUiTest.kt b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/UserFeedbackUiTest.kt index 0f84101738b..39dfae40203 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/UserFeedbackUiTest.kt +++ b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/UserFeedbackUiTest.kt @@ -1,9 +1,9 @@ package io.sentry.uitest.android import android.graphics.Color -import android.util.TypedValue import android.view.View import android.widget.EditText +import android.widget.FrameLayout import android.widget.LinearLayout import androidx.test.core.app.launchActivity import androidx.test.espresso.Espresso.onView @@ -550,10 +550,6 @@ class UserFeedbackUiTest : BaseUiTest() { assertEquals((densityScale * 12).toInt(), widget.paddingTop) assertEquals((densityScale * 12).toInt(), widget.paddingBottom) - val typedValue = TypedValue() - widget.context.theme.resolveAttribute(android.R.attr.colorForeground, typedValue, true) - assertEquals(typedValue.data, widget.currentTextColor) - assertEquals("Report a Bug", widget.text) } @@ -666,14 +662,19 @@ class UserFeedbackUiTest : BaseUiTest() { val buttonId = Int.MAX_VALUE - 1 val feedbackScenario = launchActivity() feedbackScenario.onActivity { + val layoutParams = + FrameLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT, + ) val view = - LinearLayout(it).apply { - orientation = LinearLayout.VERTICAL + FrameLayout(it).apply { addView( SentryUserFeedbackButton(it).apply { id = buttonId widgetConfig?.invoke(this) - } + }, + layoutParams, ) } it.setContentView(view) diff --git a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/ProfilingActivity.kt b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/ProfilingActivity.kt index ab63ea04b58..8626c12c6c8 100644 --- a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/ProfilingActivity.kt +++ b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/ProfilingActivity.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.View import android.widget.SeekBar import android.widget.Toast +import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager import io.sentry.ITransaction @@ -24,6 +25,21 @@ class ProfilingActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + + onBackPressedDispatcher.addCallback( + this, + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + if (profileFinished) { + isEnabled = false + onBackPressedDispatcher.onBackPressed() + } else { + Toast.makeText(this@ProfilingActivity, R.string.profiling_running, Toast.LENGTH_SHORT) + .show() + } + } + }, + ) binding = ActivityProfilingBinding.inflate(layoutInflater) binding.profilingDurationSeekbar.setOnSeekBarChangeListener( @@ -156,14 +172,6 @@ class ProfilingActivity : AppCompatActivity() { else -> fibonacci(n - 1) + fibonacci(n - 2) } - override fun onBackPressed() { - if (profileFinished) { - super.onBackPressed() - } else { - Toast.makeText(this, R.string.profiling_running, Toast.LENGTH_SHORT).show() - } - } - private fun getProfileDuration(): Float { // Minimum duration of the profile is 100 milliseconds return binding.profilingDurationSeekbar.progress / 10.0F + 0.1F