Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: expose session id #166

Merged
merged 4 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Next

- recording: expose session id ([#166](https://github.com/PostHog/posthog-android/pull/166))

## 3.5.1 - 2024-08-26

- recording: capture touch interaction off of main thread to avoid ANRs ([#165](https://github.com/PostHog/posthog-android/pull/165))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.posthog.android
import com.posthog.PostHogConfig
import com.posthog.PostHogInterface
import com.posthog.PostHogOnFeatureFlags
import java.util.UUID

public class PostHogFake : PostHogInterface {
public var event: String? = null
Expand Down Expand Up @@ -119,6 +120,10 @@ public class PostHogFake : PostHogInterface {
return false
}

override fun getSessionId(): UUID? {
return null
}

override fun <T : PostHogConfig> getConfig(): T? {
return null
}
Expand Down
12 changes: 12 additions & 0 deletions posthog/api/posthog.api
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public final class com/posthog/PostHog : com/posthog/PostHogInterface {
public fun getConfig ()Lcom/posthog/PostHogConfig;
public fun getFeatureFlag (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;
public fun getFeatureFlagPayload (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;
public fun getSessionId ()Ljava/util/UUID;
public fun group (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)V
public fun identify (Ljava/lang/String;Ljava/util/Map;Ljava/util/Map;)V
public fun isFeatureEnabled (Ljava/lang/String;Z)Z
Expand Down Expand Up @@ -38,6 +39,7 @@ public final class com/posthog/PostHog$Companion : com/posthog/PostHogInterface
public fun getConfig ()Lcom/posthog/PostHogConfig;
public fun getFeatureFlag (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;
public fun getFeatureFlagPayload (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;
public fun getSessionId ()Ljava/util/UUID;
public fun group (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)V
public fun identify (Ljava/lang/String;Ljava/util/Map;Ljava/util/Map;)V
public fun isFeatureEnabled (Ljava/lang/String;Z)Z
Expand Down Expand Up @@ -178,6 +180,7 @@ public abstract interface class com/posthog/PostHogInterface {
public abstract fun getConfig ()Lcom/posthog/PostHogConfig;
public abstract fun getFeatureFlag (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;
public abstract fun getFeatureFlagPayload (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;
public abstract fun getSessionId ()Ljava/util/UUID;
public abstract fun group (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)V
public abstract fun identify (Ljava/lang/String;Ljava/util/Map;Ljava/util/Map;)V
public abstract fun isFeatureEnabled (Ljava/lang/String;Z)Z
Expand Down Expand Up @@ -302,6 +305,15 @@ public final class com/posthog/internal/PostHogSerializer {
public final fun serializeObject (Ljava/lang/Object;)Ljava/lang/String;
}

public final class com/posthog/internal/PostHogSessionManager {
public static final field INSTANCE Lcom/posthog/internal/PostHogSessionManager;
public final fun endSession ()V
public final fun getActiveSessionId ()Ljava/util/UUID;
public final fun isSessionActive ()Z
public final fun setSessionId (Ljava/util/UUID;)V
public final fun startSession ()V
}

public final class com/posthog/internal/PostHogThreadFactory : java/util/concurrent/ThreadFactory {
public fun <init> (Ljava/lang/String;)V
public fun newThread (Ljava/lang/Runnable;)Ljava/lang/Thread;
Expand Down
60 changes: 36 additions & 24 deletions posthog/src/main/java/com/posthog/PostHog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import com.posthog.internal.PostHogPrintLogger
import com.posthog.internal.PostHogQueue
import com.posthog.internal.PostHogSendCachedEventsIntegration
import com.posthog.internal.PostHogSerializer
import com.posthog.internal.PostHogSessionManager
import com.posthog.internal.PostHogThreadFactory
import com.posthog.vendor.uuid.TimeBasedEpochGenerator
import java.util.UUID
Expand Down Expand Up @@ -48,13 +49,8 @@ public class PostHog private constructor(
private val setupLock = Any()
private val optOutLock = Any()
private val anonymousLock = Any()
private val sessionLock = Any()
private val groupsLock = Any()

// do not move to companion object, otherwise sessionId will be null
private val sessionIdNone = UUID(0, 0)

private var sessionId = sessionIdNone
private val featureFlagsCalledLock = Any()

private var config: PostHogConfig? = null
Expand Down Expand Up @@ -170,6 +166,10 @@ public class PostHog private constructor(
public override fun close() {
synchronized(setupLock) {
try {
if (!isEnabled()) {
return
}

enabled = false

config?.let { config ->
Expand Down Expand Up @@ -272,15 +272,13 @@ public class PostHog private constructor(
}
}

synchronized(sessionLock) {
if (sessionId != sessionIdNone) {
val sessionId = sessionId.toString()
props["\$session_id"] = sessionId
if (config?.sessionReplay == true) {
// Session replay requires $window_id, so we set as the same as $session_id.
// the backend might fallback to $session_id if $window_id is not present next.
props["\$window_id"] = sessionId
}
PostHogSessionManager.getActiveSessionId()?.let { sessionId ->
val tempSessionId = sessionId.toString()
props["\$session_id"] = tempSessionId
if (config?.sessionReplay == true) {
// Session replay requires $window_id, so we set as the same as $session_id.
// the backend might fallback to $session_id if $window_id is not present next.
props["\$window_id"] = tempSessionId
}
}

Expand Down Expand Up @@ -697,25 +695,35 @@ public class PostHog private constructor(
}

override fun startSession() {
synchronized(sessionLock) {
if (sessionId == sessionIdNone) {
sessionId = TimeBasedEpochGenerator.generate()
}
if (!isEnabled()) {
return
}

PostHogSessionManager.startSession()
}

override fun endSession() {
synchronized(sessionLock) {
sessionId = sessionIdNone
if (!isEnabled()) {
return
}

PostHogSessionManager.endSession()
}

override fun isSessionActive(): Boolean {
var active: Boolean
synchronized(sessionLock) {
active = sessionId != sessionIdNone
if (!isEnabled()) {
return false
}

return PostHogSessionManager.isSessionActive()
}

override fun getSessionId(): UUID? {
if (!isEnabled()) {
return null
}
return active

return PostHogSessionManager.getActiveSessionId()
}

override fun <T : PostHogConfig> getConfig(): T? {
Expand Down Expand Up @@ -896,6 +904,10 @@ public class PostHog private constructor(
return shared.isSessionActive()
}

override fun getSessionId(): UUID? {
return shared.getSessionId()
}

override fun <T : PostHogConfig> getConfig(): T? {
return shared.getConfig()
}
Expand Down
7 changes: 7 additions & 0 deletions posthog/src/main/java/com/posthog/PostHogInterface.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.posthog

import java.util.UUID

/**
* The PostHog SDK entry point
*/
Expand Down Expand Up @@ -189,6 +191,11 @@ public interface PostHogInterface {
*/
public fun isSessionActive(): Boolean

/**
* Returns the session Id if a session is active
*/
public fun getSessionId(): UUID?

@PostHogInternal
public fun <T : PostHogConfig> getConfig(): T?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.posthog.internal

import com.posthog.PostHogInternal
import com.posthog.vendor.uuid.TimeBasedEpochGenerator
import java.util.UUID

/**
* Class that manages the Session ID
*/
@PostHogInternal
public object PostHogSessionManager {
private val sessionLock = Any()

// do not move to companion object, otherwise sessionId will be null
private val sessionIdNone = UUID(0, 0)

private var sessionId = sessionIdNone

public fun startSession() {
synchronized(sessionLock) {
if (sessionId == sessionIdNone) {
sessionId = TimeBasedEpochGenerator.generate()
}
}
}

public fun endSession() {
synchronized(sessionLock) {
sessionId = sessionIdNone
}
}

public fun getActiveSessionId(): UUID? {
var tempSessionId: UUID?
synchronized(sessionLock) {
tempSessionId = if (sessionId != sessionIdNone) sessionId else null
}
return tempSessionId
}

public fun setSessionId(sessionId: UUID) {
synchronized(sessionLock) {
this.sessionId = sessionId
}
}

public fun isSessionActive(): Boolean {
var active: Boolean
synchronized(sessionLock) {
active = sessionId != sessionIdNone
}
return active
}
}
Loading