Skip to content

Commit 8e7260f

Browse files
authored
Merge 7b24e7c into 7b7964f
2 parents 7b7964f + 7b24e7c commit 8e7260f

File tree

154 files changed

+7056
-388
lines changed

Some content is hidden

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

154 files changed

+7056
-388
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+
## 7.8.0-alpha.0
4+
5+
- No documented changes.
6+
37
## 7.8.0
48

59
### Features

build.gradle.kts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ subprojects {
112112
"sentry-android-ndk",
113113
"sentry-android-okhttp",
114114
"sentry-android-sqlite",
115+
"sentry-android-replay",
115116
"sentry-android-timber"
116117
)
117118
if (jacocoAndroidModules.contains(name)) {
@@ -305,7 +306,9 @@ private val androidLibs = setOf(
305306
"sentry-android-navigation",
306307
"sentry-android-okhttp",
307308
"sentry-android-timber",
308-
"sentry-compose-android"
309+
"sentry-compose-android",
310+
"sentry-android-sqlite",
311+
"sentry-android-replay"
309312
)
310313

311314
private val androidXLibs = listOf(

buildSrc/src/main/java/Config.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ object Config {
3434

3535
val minSdkVersion = 19
3636
val minSdkVersionOkHttp = 21
37+
val minSdkVersionReplay = 19
3738
val minSdkVersionNdk = 19
3839
val minSdkVersionCompose = 21
3940
val targetSdkVersion = sdkVersion
@@ -194,6 +195,7 @@ object Config {
194195
val jsonUnit = "net.javacrumbs.json-unit:json-unit:2.32.0"
195196
val hsqldb = "org.hsqldb:hsqldb:2.6.1"
196197
val javaFaker = "com.github.javafaker:javafaker:1.0.2"
198+
val msgpack = "org.msgpack:msgpack-core:0.9.8"
197199
}
198200

199201
object QualityPlugins {

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ android.useAndroidX=true
1010
android.defaults.buildfeatures.buildconfig=true
1111

1212
# Release information
13-
versionName=7.8.0
13+
versionName=7.8.0-alpha.0
1414

1515
# Override the SDK name on native crashes on Android
1616
sentryAndroidSdkName=sentry.native.android

sentry-android-core/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ dependencies {
7676
api(projects.sentry)
7777
compileOnly(projects.sentryAndroidFragment)
7878
compileOnly(projects.sentryAndroidTimber)
79+
compileOnly(projects.sentryAndroidReplay)
7980
compileOnly(projects.sentryCompose)
8081

8182
// lifecycle processor, session tracking
@@ -103,6 +104,7 @@ dependencies {
103104
testImplementation(projects.sentryTestSupport)
104105
testImplementation(projects.sentryAndroidFragment)
105106
testImplementation(projects.sentryAndroidTimber)
107+
testImplementation(projects.sentryAndroidReplay)
106108
testImplementation(projects.sentryComposeHelper)
107109
testImplementation(projects.sentryAndroidNdk)
108110
testRuntimeOnly(Config.Libs.composeUi)

sentry-android-core/proguard-rules.pro

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,9 @@
7272
-keepnames class io.sentry.exception.SentryHttpClientException
7373

7474
##---------------End: proguard configuration for sentry-okhttp ----------
75+
76+
##---------------Begin: proguard configuration for sentry-android-replay ----------
77+
-dontwarn io.sentry.android.replay.ReplayIntegration
78+
-dontwarn io.sentry.android.replay.ReplayIntegrationKt
79+
-keepnames class io.sentry.android.replay.ReplayIntegration
80+
##---------------End: proguard configuration for sentry-android-replay ----------

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@
2222
import io.sentry.android.core.internal.util.SentryFrameMetricsCollector;
2323
import io.sentry.android.core.performance.AppStartMetrics;
2424
import io.sentry.android.fragment.FragmentLifecycleIntegration;
25+
import io.sentry.android.replay.ReplayIntegration;
2526
import io.sentry.android.timber.SentryTimberIntegration;
2627
import io.sentry.cache.PersistingOptionsObserver;
2728
import io.sentry.cache.PersistingScopeObserver;
2829
import io.sentry.compose.gestures.ComposeGestureTargetLocator;
2930
import io.sentry.compose.viewhierarchy.ComposeViewHierarchyExporter;
3031
import io.sentry.internal.gestures.GestureTargetLocator;
3132
import io.sentry.internal.viewhierarchy.ViewHierarchyExporter;
33+
import io.sentry.transport.CurrentDateProvider;
3234
import io.sentry.transport.NoOpEnvelopeCache;
3335
import io.sentry.util.LazyEvaluator;
3436
import io.sentry.util.Objects;
@@ -237,7 +239,8 @@ static void installDefaultIntegrations(
237239
final @NotNull LoadClass loadClass,
238240
final @NotNull ActivityFramesTracker activityFramesTracker,
239241
final boolean isFragmentAvailable,
240-
final boolean isTimberAvailable) {
242+
final boolean isTimberAvailable,
243+
final boolean isReplayAvailable) {
241244

242245
// Integration MUST NOT cache option values in ctor, as they will be configured later by the
243246
// user
@@ -302,6 +305,12 @@ static void installDefaultIntegrations(
302305
new NetworkBreadcrumbsIntegration(context, buildInfoProvider, options.getLogger()));
303306
options.addIntegration(new TempSensorBreadcrumbsIntegration(context));
304307
options.addIntegration(new PhoneStateBreadcrumbsIntegration(context));
308+
if (isReplayAvailable) {
309+
final ReplayIntegration replay =
310+
new ReplayIntegration(context, CurrentDateProvider.getInstance());
311+
options.addIntegration(replay);
312+
options.setReplayController(replay);
313+
}
305314
}
306315

307316
/**

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io.sentry.SentryBaseEvent;
1111
import io.sentry.SentryEvent;
1212
import io.sentry.SentryLevel;
13+
import io.sentry.SentryReplayEvent;
1314
import io.sentry.android.core.internal.util.AndroidMainThreadChecker;
1415
import io.sentry.android.core.performance.AppStartMetrics;
1516
import io.sentry.android.core.performance.TimeSpan;
@@ -303,4 +304,17 @@ private void setSideLoadedInfo(final @NotNull SentryBaseEvent event) {
303304

304305
return transaction;
305306
}
307+
308+
@Override
309+
public @NotNull SentryReplayEvent process(
310+
final @NotNull SentryReplayEvent event, final @NotNull Hint hint) {
311+
final boolean applyScopeData = shouldApplyScopeData(event, hint);
312+
if (applyScopeData) {
313+
processNonCachedEvent(event, hint);
314+
}
315+
316+
setCommons(event, false, applyScopeData);
317+
318+
return event;
319+
}
306320
}

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

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import io.sentry.transport.ICurrentDateProvider;
1212
import java.util.Timer;
1313
import java.util.TimerTask;
14+
import java.util.concurrent.atomic.AtomicBoolean;
1415
import java.util.concurrent.atomic.AtomicLong;
1516
import org.jetbrains.annotations.NotNull;
1617
import org.jetbrains.annotations.Nullable;
@@ -19,11 +20,12 @@
1920
final class LifecycleWatcher implements DefaultLifecycleObserver {
2021

2122
private final AtomicLong lastUpdatedSession = new AtomicLong(0L);
23+
private final AtomicBoolean isFreshSession = new AtomicBoolean(false);
2224

2325
private final long sessionIntervalMillis;
2426

2527
private @Nullable TimerTask timerTask;
26-
private final @Nullable Timer timer;
28+
private final @NotNull Timer timer = new Timer(true);
2729
private final @NotNull Object timerLock = new Object();
2830
private final @NotNull IHub hub;
2931
private final boolean enableSessionTracking;
@@ -55,11 +57,6 @@ final class LifecycleWatcher implements DefaultLifecycleObserver {
5557
this.enableAppLifecycleBreadcrumbs = enableAppLifecycleBreadcrumbs;
5658
this.hub = hub;
5759
this.currentDateProvider = currentDateProvider;
58-
if (enableSessionTracking) {
59-
timer = new Timer(true);
60-
} else {
61-
timer = null;
62-
}
6360
}
6461

6562
// App goes to foreground
@@ -74,41 +71,46 @@ public void onStart(final @NotNull LifecycleOwner owner) {
7471
}
7572

7673
private void startSession() {
77-
if (enableSessionTracking) {
78-
cancelTask();
74+
cancelTask();
7975

80-
final long currentTimeMillis = currentDateProvider.getCurrentTimeMillis();
76+
final long currentTimeMillis = currentDateProvider.getCurrentTimeMillis();
8177

82-
hub.configureScope(
83-
scope -> {
84-
if (lastUpdatedSession.get() == 0L) {
85-
final @Nullable Session currentSession = scope.getSession();
86-
if (currentSession != null && currentSession.getStarted() != null) {
87-
lastUpdatedSession.set(currentSession.getStarted().getTime());
88-
}
78+
hub.configureScope(
79+
scope -> {
80+
if (lastUpdatedSession.get() == 0L) {
81+
final @Nullable Session currentSession = scope.getSession();
82+
if (currentSession != null && currentSession.getStarted() != null) {
83+
lastUpdatedSession.set(currentSession.getStarted().getTime());
84+
isFreshSession.set(true);
8985
}
90-
});
86+
}
87+
});
9188

92-
final long lastUpdatedSession = this.lastUpdatedSession.get();
93-
if (lastUpdatedSession == 0L
94-
|| (lastUpdatedSession + sessionIntervalMillis) <= currentTimeMillis) {
89+
final long lastUpdatedSession = this.lastUpdatedSession.get();
90+
if (lastUpdatedSession == 0L
91+
|| (lastUpdatedSession + sessionIntervalMillis) <= currentTimeMillis) {
92+
if (enableSessionTracking) {
9593
addSessionBreadcrumb("start");
9694
hub.startSession();
9795
}
98-
this.lastUpdatedSession.set(currentTimeMillis);
96+
hub.getOptions().getReplayController().start();
97+
} else if (!isFreshSession.get()) {
98+
// only resume if it's not a fresh session, which has been started in SentryAndroid.init
99+
hub.getOptions().getReplayController().resume();
99100
}
101+
isFreshSession.set(false);
102+
this.lastUpdatedSession.set(currentTimeMillis);
100103
}
101104

102105
// App went to background and triggered this callback after 700ms
103106
// as no new screen was shown
104107
@Override
105108
public void onStop(final @NotNull LifecycleOwner owner) {
106-
if (enableSessionTracking) {
107-
final long currentTimeMillis = currentDateProvider.getCurrentTimeMillis();
108-
this.lastUpdatedSession.set(currentTimeMillis);
109+
final long currentTimeMillis = currentDateProvider.getCurrentTimeMillis();
110+
this.lastUpdatedSession.set(currentTimeMillis);
109111

110-
scheduleEndSession();
111-
}
112+
hub.getOptions().getReplayController().pause();
113+
scheduleEndSession();
112114

113115
AppState.getInstance().setInBackground(true);
114116
addAppBreadcrumb("background");
@@ -122,8 +124,11 @@ private void scheduleEndSession() {
122124
new TimerTask() {
123125
@Override
124126
public void run() {
125-
addSessionBreadcrumb("end");
126-
hub.endSession();
127+
if (enableSessionTracking) {
128+
addSessionBreadcrumb("end");
129+
hub.endSession();
130+
}
131+
hub.getOptions().getReplayController().stop();
127132
}
128133
};
129134

@@ -164,7 +169,7 @@ TimerTask getTimerTask() {
164169
}
165170

166171
@TestOnly
167-
@Nullable
172+
@NotNull
168173
Timer getTimer() {
169174
return timer;
170175
}

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,14 @@ final class ManifestMetadataReader {
104104

105105
static final String ENABLE_METRICS = "io.sentry.enable-metrics";
106106

107+
static final String REPLAYS_SESSION_SAMPLE_RATE = "io.sentry.session-replay.session-sample-rate";
108+
109+
static final String REPLAYS_ERROR_SAMPLE_RATE = "io.sentry.session-replay.error-sample-rate";
110+
111+
static final String REPLAYS_REDACT_ALL_TEXT = "io.sentry.session-replay.redact-all-text";
112+
113+
static final String REPLAYS_REDACT_ALL_IMAGES = "io.sentry.session-replay.redact-all-images";
114+
107115
/** ManifestMetadataReader ctor */
108116
private ManifestMetadataReader() {}
109117

@@ -382,6 +390,41 @@ static void applyMetadata(
382390

383391
options.setEnableMetrics(
384392
readBool(metadata, logger, ENABLE_METRICS, options.isEnableMetrics()));
393+
394+
if (options.getExperimental().getSessionReplay().getSessionSampleRate() == null) {
395+
final Double sessionSampleRate =
396+
readDouble(metadata, logger, REPLAYS_SESSION_SAMPLE_RATE);
397+
if (sessionSampleRate != -1) {
398+
options.getExperimental().getSessionReplay().setSessionSampleRate(sessionSampleRate);
399+
}
400+
}
401+
402+
if (options.getExperimental().getSessionReplay().getErrorSampleRate() == null) {
403+
final Double errorSampleRate = readDouble(metadata, logger, REPLAYS_ERROR_SAMPLE_RATE);
404+
if (errorSampleRate != -1) {
405+
options.getExperimental().getSessionReplay().setErrorSampleRate(errorSampleRate);
406+
}
407+
}
408+
409+
options
410+
.getExperimental()
411+
.getSessionReplay()
412+
.setRedactAllText(
413+
readBool(
414+
metadata,
415+
logger,
416+
REPLAYS_REDACT_ALL_TEXT,
417+
options.getExperimental().getSessionReplay().getRedactAllText()));
418+
419+
options
420+
.getExperimental()
421+
.getSessionReplay()
422+
.setRedactAllImages(
423+
readBool(
424+
metadata,
425+
logger,
426+
REPLAYS_REDACT_ALL_IMAGES,
427+
options.getExperimental().getSessionReplay().getRedactAllImages()));
385428
}
386429

387430
options

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ public final class SentryAndroid {
3333
static final String SENTRY_TIMBER_INTEGRATION_CLASS_NAME =
3434
"io.sentry.android.timber.SentryTimberIntegration";
3535

36+
static final String SENTRY_REPLAY_INTEGRATION_CLASS_NAME =
37+
"io.sentry.android.replay.ReplayIntegration";
38+
39+
private static boolean isReplayAvailable = false;
40+
3641
private static final String TIMBER_CLASS_NAME = "timber.log.Timber";
3742
private static final String FRAGMENT_CLASS_NAME =
3843
"androidx.fragment.app.FragmentManager$FragmentLifecycleCallbacks";
@@ -99,6 +104,8 @@ public static synchronized void init(
99104
final boolean isTimberAvailable =
100105
(isTimberUpstreamAvailable
101106
&& classLoader.isClassAvailable(SENTRY_TIMBER_INTEGRATION_CLASS_NAME, options));
107+
isReplayAvailable =
108+
classLoader.isClassAvailable(SENTRY_REPLAY_INTEGRATION_CLASS_NAME, options);
102109

103110
final BuildInfoProvider buildInfoProvider = new BuildInfoProvider(logger);
104111
final LoadClass loadClass = new LoadClass();
@@ -118,7 +125,8 @@ public static synchronized void init(
118125
loadClass,
119126
activityFramesTracker,
120127
isFragmentAvailable,
121-
isTimberAvailable);
128+
isTimberAvailable,
129+
isReplayAvailable);
122130

123131
configuration.configure(options);
124132

@@ -145,9 +153,12 @@ public static synchronized void init(
145153
true);
146154

147155
final @NotNull IHub hub = Sentry.getCurrentHub();
148-
if (hub.getOptions().isEnableAutoSessionTracking() && ContextUtils.isForegroundImportance()) {
149-
hub.addBreadcrumb(BreadcrumbFactory.forSession("session.start"));
150-
hub.startSession();
156+
if (ContextUtils.isForegroundImportance()) {
157+
if (hub.getOptions().isEnableAutoSessionTracking()) {
158+
hub.addBreadcrumb(BreadcrumbFactory.forSession("session.start"));
159+
hub.startSession();
160+
}
161+
hub.getOptions().getReplayController().start();
151162
}
152163
} catch (IllegalAccessException e) {
153164
logger.log(SentryLevel.FATAL, "Fatal error during SentryAndroid.init(...)", e);

0 commit comments

Comments
 (0)