Skip to content

Commit 81bed63

Browse files
authored
Hook User Interaction integration into running Activity in case of deferred SDK init (#4337)
* Hook User Interaction integration into running Activity in case of deferred SDK init * Update Changelog * Merge CurrentActivityIntegration into AppStartMetrics * Fix Changelog
1 parent 0f2e104 commit 81bed63

12 files changed

+176
-227
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
### Fixes
6+
7+
- Hook User Interaction integration into running Activity in case of deferred SDK init ([#4337](https://github.com/getsentry/sentry-java/pull/4337))
8+
59
### Dependencies
610

711
- Bump Gradle from v8.13 to v8.14.0 ([#4360](https://github.com/getsentry/sentry-java/pull/4360))

sentry-android-core/api/sentry-android-core.api

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -198,24 +198,12 @@ public final class io/sentry/android/core/ContextUtils {
198198

199199
public class io/sentry/android/core/CurrentActivityHolder {
200200
public fun clearActivity ()V
201+
public fun clearActivity (Landroid/app/Activity;)V
201202
public fun getActivity ()Landroid/app/Activity;
202203
public static fun getInstance ()Lio/sentry/android/core/CurrentActivityHolder;
203204
public fun setActivity (Landroid/app/Activity;)V
204205
}
205206

206-
public final class io/sentry/android/core/CurrentActivityIntegration : android/app/Application$ActivityLifecycleCallbacks, io/sentry/Integration, java/io/Closeable {
207-
public fun <init> (Landroid/app/Application;)V
208-
public fun close ()V
209-
public fun onActivityCreated (Landroid/app/Activity;Landroid/os/Bundle;)V
210-
public fun onActivityDestroyed (Landroid/app/Activity;)V
211-
public fun onActivityPaused (Landroid/app/Activity;)V
212-
public fun onActivityResumed (Landroid/app/Activity;)V
213-
public fun onActivitySaveInstanceState (Landroid/app/Activity;Landroid/os/Bundle;)V
214-
public fun onActivityStarted (Landroid/app/Activity;)V
215-
public fun onActivityStopped (Landroid/app/Activity;)V
216-
public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V
217-
}
218-
219207
public final class io/sentry/android/core/DeviceInfoUtil {
220208
public fun <init> (Landroid/content/Context;Lio/sentry/android/core/SentryAndroidOptions;)V
221209
public fun collectDeviceInformation (ZZ)Lio/sentry/protocol/Device;
@@ -502,7 +490,10 @@ public class io/sentry/android/core/performance/AppStartMetrics : io/sentry/andr
502490
public fun isAppLaunchedInForeground ()Z
503491
public fun onActivityCreated (Landroid/app/Activity;Landroid/os/Bundle;)V
504492
public fun onActivityDestroyed (Landroid/app/Activity;)V
493+
public fun onActivityPaused (Landroid/app/Activity;)V
494+
public fun onActivityResumed (Landroid/app/Activity;)V
505495
public fun onActivityStarted (Landroid/app/Activity;)V
496+
public fun onActivityStopped (Landroid/app/Activity;)V
506497
public fun onAppStartSpansSent ()V
507498
public static fun onApplicationCreate (Landroid/app/Application;)V
508499
public static fun onApplicationPostCreate (Landroid/app/Application;)V

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,6 @@ static void installDefaultIntegrations(
363363
new ActivityLifecycleIntegration(
364364
(Application) context, buildInfoProvider, activityFramesTracker));
365365
options.addIntegration(new ActivityBreadcrumbsIntegration((Application) context));
366-
options.addIntegration(new CurrentActivityIntegration((Application) context));
367366
options.addIntegration(new UserInteractionIntegration((Application) context, loadClass));
368367
if (isFragmentAvailable) {
369368
options.addIntegration(new FragmentLifecycleIntegration((Application) context, true, true));

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package io.sentry.android.core;
22

33
import android.app.Activity;
4-
import androidx.annotation.NonNull;
5-
import androidx.annotation.Nullable;
64
import java.lang.ref.WeakReference;
75
import org.jetbrains.annotations.ApiStatus;
86
import org.jetbrains.annotations.NotNull;
7+
import org.jetbrains.annotations.Nullable;
98

109
@ApiStatus.Internal
1110
public class CurrentActivityHolder {
@@ -16,7 +15,7 @@ private CurrentActivityHolder() {}
1615

1716
private @Nullable WeakReference<Activity> currentActivity;
1817

19-
public static @NonNull CurrentActivityHolder getInstance() {
18+
public static @NotNull CurrentActivityHolder getInstance() {
2019
return instance;
2120
}
2221

@@ -27,7 +26,7 @@ private CurrentActivityHolder() {}
2726
return null;
2827
}
2928

30-
public void setActivity(final @NonNull Activity activity) {
29+
public void setActivity(final @NotNull Activity activity) {
3130
if (currentActivity != null && currentActivity.get() == activity) {
3231
return;
3332
}
@@ -38,4 +37,11 @@ public void setActivity(final @NonNull Activity activity) {
3837
public void clearActivity() {
3938
currentActivity = null;
4039
}
40+
41+
public void clearActivity(final @NotNull Activity activity) {
42+
if (currentActivity != null && currentActivity.get() != activity) {
43+
return;
44+
}
45+
currentActivity = null;
46+
}
4147
}

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

Lines changed: 0 additions & 85 deletions
This file was deleted.

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import android.app.Application;
77
import android.os.Bundle;
88
import android.view.Window;
9+
import androidx.lifecycle.Lifecycle;
10+
import androidx.lifecycle.LifecycleOwner;
911
import io.sentry.IScopes;
1012
import io.sentry.Integration;
1113
import io.sentry.SentryLevel;
@@ -27,12 +29,15 @@ public final class UserInteractionIntegration
2729
private @Nullable SentryAndroidOptions options;
2830

2931
private final boolean isAndroidXAvailable;
32+
private final boolean isAndroidxLifecycleAvailable;
3033

3134
public UserInteractionIntegration(
3235
final @NotNull Application application, final @NotNull io.sentry.util.LoadClass classLoader) {
3336
this.application = Objects.requireNonNull(application, "Application is required");
3437
isAndroidXAvailable =
3538
classLoader.isClassAvailable("androidx.core.view.GestureDetectorCompat", options);
39+
isAndroidxLifecycleAvailable =
40+
classLoader.isClassAvailable("androidx.lifecycle.Lifecycle", options);
3641
}
3742

3843
private void startTracking(final @NotNull Activity activity) {
@@ -127,6 +132,17 @@ public void register(@NotNull IScopes scopes, @NotNull SentryOptions options) {
127132
application.registerActivityLifecycleCallbacks(this);
128133
this.options.getLogger().log(SentryLevel.DEBUG, "UserInteractionIntegration installed.");
129134
addIntegrationToSdkVersion("UserInteraction");
135+
136+
// In case of a deferred init, we hook into any resumed activity
137+
if (isAndroidxLifecycleAvailable) {
138+
final @Nullable Activity activity = CurrentActivityHolder.getInstance().getActivity();
139+
if (activity instanceof LifecycleOwner) {
140+
if (((LifecycleOwner) activity).getLifecycle().getCurrentState()
141+
== Lifecycle.State.RESUMED) {
142+
startTracking(activity);
143+
}
144+
}
145+
}
130146
} else {
131147
options
132148
.getLogger()

sentry-android-core/src/main/java/io/sentry/android/core/performance/AppStartMetrics.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import io.sentry.TracesSamplingDecision;
1818
import io.sentry.android.core.BuildInfoProvider;
1919
import io.sentry.android.core.ContextUtils;
20+
import io.sentry.android.core.CurrentActivityHolder;
2021
import io.sentry.android.core.SentryAndroidOptions;
2122
import io.sentry.android.core.internal.util.FirstDrawDoneListener;
2223
import io.sentry.util.AutoClosableReentrantLock;
@@ -42,7 +43,6 @@
4243
*/
4344
@ApiStatus.Internal
4445
public class AppStartMetrics extends ActivityLifecycleCallbacksAdapter {
45-
4646
public enum AppStartType {
4747
UNKNOWN,
4848
COLD,
@@ -342,10 +342,12 @@ private void checkCreateTimeOnMain() {
342342

343343
@Override
344344
public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
345-
final long nowUptimeMs = SystemClock.uptimeMillis();
345+
CurrentActivityHolder.getInstance().setActivity(activity);
346346

347347
// the first activity determines the app start type
348348
if (activeActivitiesCounter.incrementAndGet() == 1 && !firstDrawDone.get()) {
349+
final long nowUptimeMs = SystemClock.uptimeMillis();
350+
349351
// If the app (process) was launched more than 1 minute ago, it's likely wrong
350352
final long durationSinceAppStartMillis = nowUptimeMs - appStartSpan.getStartUptimeMs();
351353
if (!appLaunchedInForeground || durationSinceAppStartMillis > TimeUnit.MINUTES.toMillis(1)) {
@@ -367,6 +369,8 @@ public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle saved
367369

368370
@Override
369371
public void onActivityStarted(@NonNull Activity activity) {
372+
CurrentActivityHolder.getInstance().setActivity(activity);
373+
370374
if (firstDrawDone.get()) {
371375
return;
372376
}
@@ -378,8 +382,25 @@ public void onActivityStarted(@NonNull Activity activity) {
378382
}
379383
}
380384

385+
@Override
386+
public void onActivityResumed(@NonNull Activity activity) {
387+
CurrentActivityHolder.getInstance().setActivity(activity);
388+
}
389+
390+
@Override
391+
public void onActivityPaused(@NonNull Activity activity) {
392+
CurrentActivityHolder.getInstance().clearActivity(activity);
393+
}
394+
395+
@Override
396+
public void onActivityStopped(@NonNull Activity activity) {
397+
CurrentActivityHolder.getInstance().clearActivity(activity);
398+
}
399+
381400
@Override
382401
public void onActivityDestroyed(@NonNull Activity activity) {
402+
CurrentActivityHolder.getInstance().clearActivity(activity);
403+
383404
final int remainingActivities = activeActivitiesCounter.decrementAndGet();
384405
// if the app is moving into background
385406
// as the next Activity is considered like a new app start

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

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -634,15 +634,6 @@ class AndroidOptionsInitializerTest {
634634
assertTrue { fixture.sentryOptions.envelopeDiskCache is AndroidEnvelopeCache }
635635
}
636636

637-
@Test
638-
fun `CurrentActivityIntegration is added by default`() {
639-
fixture.initSut(useRealContext = true)
640-
641-
val actual =
642-
fixture.sentryOptions.integrations.firstOrNull { it is CurrentActivityIntegration }
643-
assertNotNull(actual)
644-
}
645-
646637
@Test
647638
fun `When Activity Frames Tracking is enabled, the Activity Frames Tracker should be unavailable`() {
648639
fixture.initSut(

0 commit comments

Comments
 (0)