Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ public open class PostHogConfig constructor(
encryption = encryption,
onFeatureFlags = onFeatureFlags,
proxy = proxy,
remoteConfigProvider = { config, api, _ ->
remoteConfigProvider = { config, api, _, _ ->
PostHogFeatureFlags(
config,
api,
Expand Down
1 change: 1 addition & 0 deletions posthog/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## Next

- feat: Add ability to cache properties for flags ([#315](https://github.com/PostHog/posthog-android/pull/315))
- fix: Typed `groupProperties` and `userProperties` types to match the API and other SDKs ([#312](https://github.com/PostHog/posthog-android/pull/312))

## 4.2.0 - 2025-10-23
Expand Down
27 changes: 23 additions & 4 deletions posthog/api/posthog.api
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,14 @@ public final class com/posthog/PostHog : com/posthog/PostHogStateless, com/posth
public fun register (Ljava/lang/String;Ljava/lang/Object;)V
public fun reloadFeatureFlags (Lcom/posthog/PostHogOnFeatureFlags;)V
public fun reset ()V
public final fun resetGroupPropertiesForFlags (Ljava/lang/String;)V
public static synthetic fun resetGroupPropertiesForFlags$default (Lcom/posthog/PostHog;Ljava/lang/String;ILjava/lang/Object;)V
public final fun resetPersonPropertiesForFlags ()V
public fun screen (Ljava/lang/String;Ljava/util/Map;)V
public final fun setGroupPropertiesForFlags (Ljava/lang/String;Ljava/util/Map;Z)V
public static synthetic fun setGroupPropertiesForFlags$default (Lcom/posthog/PostHog;Ljava/lang/String;Ljava/util/Map;ZILjava/lang/Object;)V
public final fun setPersonPropertiesForFlags (Ljava/util/Map;Z)V
public static synthetic fun setPersonPropertiesForFlags$default (Lcom/posthog/PostHog;Ljava/util/Map;ZILjava/lang/Object;)V
public fun setup (Lcom/posthog/PostHogConfig;)V
public fun startSession ()V
public fun startSessionReplay (Z)V
Expand Down Expand Up @@ -86,8 +93,8 @@ public class com/posthog/PostHogConfig {
public static final field DEFAULT_HOST Ljava/lang/String;
public static final field DEFAULT_US_ASSETS_HOST Ljava/lang/String;
public static final field DEFAULT_US_HOST Ljava/lang/String;
public fun <init> (Ljava/lang/String;Ljava/lang/String;ZZZIZLjava/util/List;ZIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;ZLcom/posthog/PostHogPropertiesSanitizer;Lkotlin/jvm/functions/Function1;ZLcom/posthog/PersonProfiles;ZLjava/net/Proxy;Lcom/posthog/surveys/PostHogSurveysConfig;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function5;Lcom/posthog/errortracking/PostHogErrorTrackingConfig;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;ZZZIZLjava/util/List;ZIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;ZLcom/posthog/PostHogPropertiesSanitizer;Lkotlin/jvm/functions/Function1;ZLcom/posthog/PersonProfiles;ZLjava/net/Proxy;Lcom/posthog/surveys/PostHogSurveysConfig;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function5;Lcom/posthog/errortracking/PostHogErrorTrackingConfig;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;ZZZIZLjava/util/List;ZZIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;ZLcom/posthog/PostHogPropertiesSanitizer;Lkotlin/jvm/functions/Function1;ZLcom/posthog/PersonProfiles;ZLjava/net/Proxy;Lcom/posthog/surveys/PostHogSurveysConfig;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Lcom/posthog/errortracking/PostHogErrorTrackingConfig;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;ZZZIZLjava/util/List;ZZIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;ZLcom/posthog/PostHogPropertiesSanitizer;Lkotlin/jvm/functions/Function1;ZLcom/posthog/PersonProfiles;ZLjava/net/Proxy;Lcom/posthog/surveys/PostHogSurveysConfig;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Lcom/posthog/errortracking/PostHogErrorTrackingConfig;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun addBeforeSend (Lcom/posthog/PostHogBeforeSend;)V
public final fun addIntegration (Lcom/posthog/PostHogIntegration;)V
public final fun getApiKey ()Ljava/lang/String;
Expand Down Expand Up @@ -118,14 +125,15 @@ public class com/posthog/PostHogConfig {
public final fun getProxy ()Ljava/net/Proxy;
public final fun getQueueProvider ()Lkotlin/jvm/functions/Function5;
public final fun getRemoteConfig ()Z
public final fun getRemoteConfigProvider ()Lkotlin/jvm/functions/Function3;
public final fun getRemoteConfigProvider ()Lkotlin/jvm/functions/Function4;
public final fun getReplayStoragePrefix ()Ljava/lang/String;
public final fun getReuseAnonymousId ()Z
public final fun getSdkName ()Ljava/lang/String;
public final fun getSdkVersion ()Ljava/lang/String;
public final fun getSendFeatureFlagEvent ()Z
public final fun getSerializer ()Lcom/posthog/internal/PostHogSerializer;
public final fun getSessionReplay ()Z
public final fun getSetDefaultPersonProperties ()Z
public final fun getSnapshotEndpoint ()Ljava/lang/String;
public final fun getStoragePrefix ()Ljava/lang/String;
public final fun getSurveys ()Z
Expand Down Expand Up @@ -160,6 +168,7 @@ public class com/posthog/PostHogConfig {
public final fun setSdkVersion (Ljava/lang/String;)V
public final fun setSendFeatureFlagEvent (Z)V
public final fun setSessionReplay (Z)V
public final fun setSetDefaultPersonProperties (Z)V
public final fun setSnapshotEndpoint (Ljava/lang/String;)V
public final fun setStoragePrefix (Ljava/lang/String;)V
public final fun setSurveys (Z)V
Expand Down Expand Up @@ -483,6 +492,10 @@ public abstract interface class com/posthog/internal/PostHogContext {
public abstract fun getStaticContext ()Ljava/util/Map;
}

public final class com/posthog/internal/PostHogContextKt {
public static final fun personPropertiesContext (Lcom/posthog/internal/PostHogContext;)Ljava/util/Map;
}

public abstract interface class com/posthog/internal/PostHogDateProvider {
public abstract fun addSecondsToCurrentDate (I)Ljava/util/Date;
public abstract fun currentDate ()Ljava/util/Date;
Expand Down Expand Up @@ -604,7 +617,8 @@ public abstract interface class com/posthog/internal/PostHogQueueInterface {
}

public final class com/posthog/internal/PostHogRemoteConfig : com/posthog/internal/PostHogFeatureFlagsInterface {
public fun <init> (Lcom/posthog/PostHogConfig;Lcom/posthog/internal/PostHogApi;Ljava/util/concurrent/ExecutorService;)V
public fun <init> (Lcom/posthog/PostHogConfig;Lcom/posthog/internal/PostHogApi;Ljava/util/concurrent/ExecutorService;Lkotlin/jvm/functions/Function0;)V
public synthetic fun <init> (Lcom/posthog/PostHogConfig;Lcom/posthog/internal/PostHogApi;Ljava/util/concurrent/ExecutorService;Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun clear ()V
public fun getFeatureFlag (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;)Ljava/lang/Object;
public fun getFeatureFlagPayload (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;)Ljava/lang/Object;
Expand All @@ -618,7 +632,12 @@ public final class com/posthog/internal/PostHogRemoteConfig : com/posthog/intern
public static synthetic fun loadFeatureFlags$default (Lcom/posthog/internal/PostHogRemoteConfig;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lcom/posthog/PostHogOnFeatureFlags;Lcom/posthog/PostHogOnFeatureFlags;ILjava/lang/Object;)V
public final fun loadRemoteConfig (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lcom/posthog/PostHogOnFeatureFlags;Lcom/posthog/PostHogOnFeatureFlags;)V
public static synthetic fun loadRemoteConfig$default (Lcom/posthog/internal/PostHogRemoteConfig;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Lcom/posthog/PostHogOnFeatureFlags;Lcom/posthog/PostHogOnFeatureFlags;ILjava/lang/Object;)V
public final fun resetGroupPropertiesForFlags (Ljava/lang/String;)V
public static synthetic fun resetGroupPropertiesForFlags$default (Lcom/posthog/internal/PostHogRemoteConfig;Ljava/lang/String;ILjava/lang/Object;)V
public final fun resetPersonPropertiesForFlags ()V
public final fun setGroupPropertiesForFlags (Ljava/lang/String;Ljava/util/Map;)V
public final fun setOnRemoteConfigLoaded (Lkotlin/jvm/functions/Function0;)V
public final fun setPersonPropertiesForFlags (Ljava/util/Map;)V
}

public class com/posthog/internal/PostHogRemoteConfigResponse {
Expand Down
121 changes: 120 additions & 1 deletion posthog/src/main/java/com/posthog/PostHog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.posthog.internal.PostHogSerializer
import com.posthog.internal.PostHogSessionManager
import com.posthog.internal.PostHogThreadFactory
import com.posthog.internal.errortracking.ThrowableCoercer
import com.posthog.internal.personPropertiesContext
import com.posthog.internal.replay.PostHogSessionReplayHandler
import com.posthog.internal.surveys.PostHogSurveysHandler
import com.posthog.vendor.uuid.TimeBasedEpochGenerator
Expand Down Expand Up @@ -96,7 +97,10 @@ public class PostHog private constructor(
val api = PostHogApi(config)
val queue = config.queueProvider(config, api, PostHogApiEndpoint.BATCH, config.storagePrefix, queueExecutor)
val replayQueue = config.queueProvider(config, api, PostHogApiEndpoint.SNAPSHOT, config.replayStoragePrefix, replayExecutor)
val featureFlags = config.remoteConfigProvider(config, api, remoteConfigExecutor)
val featureFlags =
config.remoteConfigProvider(config, api, remoteConfigExecutor) {
getDefaultPersonProperties()
}

// no need to lock optOut here since the setup is locked already
val optOut =
Expand Down Expand Up @@ -423,6 +427,9 @@ public class PostHog private constructor(
requirePersonProcessing("capture", ignoreMessage = true)
}

// Automatically set person properties for feature flags during capture event
setPersonPropertiesForFlagsIfNeeded(userProperties, userPropertiesSetOnce)

if (newDistinctId.isBlank()) {
config?.logger?.log("capture call not allowed, distinctId is invalid: $newDistinctId.")
return
Expand Down Expand Up @@ -579,6 +586,45 @@ public class PostHog private constructor(
capture(PostHogEventName.CREATE_ALIAS.event, properties = props)
}

/**
* Returns fresh default device and app properties for feature flag evaluation.
*/
private fun getDefaultPersonProperties(): Map<String, Any> {
if (!isEnabled()) return emptyMap()
if (config?.setDefaultPersonProperties != true) return emptyMap()

return config?.context?.personPropertiesContext() ?: emptyMap()
}

private fun setPersonPropertiesForFlagsIfNeeded(
userProperties: Map<String, Any>?,
userPropertiesSetOnce: Map<String, Any>? = null,
Comment on lines +592 to +601
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after #312
should this be Map<String, Any?> instead? (Any?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nullable keys are needed for local evaluation. In this case, we're always evaluating remotely so we don't need to handle null values.

) {
if (!hasPersonProcessing()) return
if (userProperties.isNullOrEmpty() && userPropertiesSetOnce.isNullOrEmpty()) return

val allProperties = mutableMapOf<String, Any>()
userPropertiesSetOnce?.let {
allProperties.putAll(userPropertiesSetOnce)
}
userProperties?.let {
// User properties override setOnce properties
allProperties.putAll(userProperties)
}

remoteConfig?.setPersonPropertiesForFlags(allProperties)
}

private fun setGroupPropertiesForFlagsIfNeeded(
type: String,
groupProperties: Map<String, Any>?,
) {
if (!hasPersonProcessing()) return
if (groupProperties.isNullOrEmpty()) return

remoteConfig?.setGroupPropertiesForFlags(type, groupProperties)
}

public override fun identify(
distinctId: String,
userProperties: Map<String, Any>?,
Expand Down Expand Up @@ -636,6 +682,9 @@ public class PostHog private constructor(
}
this.distinctId = distinctId

// Automatically set person properties for feature flags during identify() call
setPersonPropertiesForFlagsIfNeeded(userProperties, userPropertiesSetOnce)

// only because of testing in isolation, this flag is always enabled
if (reloadFeatureFlags) {
reloadFeatureFlags(config?.onFeatureFlags)
Expand Down Expand Up @@ -745,6 +794,9 @@ public class PostHog private constructor(

super.groupStateless(this.distinctId, type, key, groupProperties)

// Automatically set group properties for feature flags
setGroupPropertiesForFlagsIfNeeded(type, groupProperties)

// only because of testing in isolation, this flag is always enabled
if (reloadFeatureFlags && reloadFeatureFlagsIfNewGroup) {
reloadFeatureFlags(config?.onFeatureFlags)
Expand Down Expand Up @@ -887,6 +939,73 @@ public class PostHog private constructor(
replayQueue?.flush()
}

/**
* Sets person properties that will be included in feature flag evaluation requests.
*
* @param properties Dictionary of person properties to include in flag evaluation
* @param reloadFeatureFlags Whether to automatically reload feature flags after setting properties
*/
public fun setPersonPropertiesForFlags(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should these methods be part of PostHogInterface or PostHogCoreInterface?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! I've added them to PostHogInterface

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

properties: Map<String, Any>,
reloadFeatureFlags: Boolean = true,
) {
if (!isEnabled()) return
if (!hasPersonProcessing()) return
if (properties.isEmpty()) return

remoteConfig?.setPersonPropertiesForFlags(properties)

if (reloadFeatureFlags && this.reloadFeatureFlags) {
this.reloadFeatureFlags()
}
}

/**
* Resets all person properties that were set for feature flag evaluation.
*/
public fun resetPersonPropertiesForFlags() {
if (!isEnabled()) return
if (!hasPersonProcessing()) return

remoteConfig?.resetPersonPropertiesForFlags()
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should reloadFeatureFlags here as well right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right. This is a deviation from the iOS implementation. Similar to the other methods added here, I've added a reloadFeatureFlags boolean (default to true) which determines whether or not flags will be reloaded upon calling the method.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the user intentionally resets, we should otherwise the flags will be evaluated wrongly.
same on iOS, do I see a PR? :D


/**
* Sets properties for a specific group type to include when evaluating feature flags.
*
* @param groupType The group type identifier (e.g., "organization", "team")
* @param properties Dictionary of properties to set for this group type
* @param reloadFeatureFlags Whether to automatically reload feature flags after setting properties
*/
public fun setGroupPropertiesForFlags(
groupType: String,
properties: Map<String, Any>,
reloadFeatureFlags: Boolean = true,
) {
if (!isEnabled()) return
if (!hasPersonProcessing()) return

if (properties.isEmpty()) return

remoteConfig?.setGroupPropertiesForFlags(groupType, properties)

if (reloadFeatureFlags && this.reloadFeatureFlags) {
this.reloadFeatureFlags()
}
}

/**
* Clears group properties for feature flag evaluation.
*
* @param groupType Optional group type to clear. If null, clears all group properties.
*/
public fun resetGroupPropertiesForFlags(groupType: String? = null) {
if (!isEnabled()) return
if (!hasPersonProcessing()) return

remoteConfig?.resetGroupPropertiesForFlags(groupType)
}

public override fun reset() {
if (!isEnabled()) {
return
Expand Down
35 changes: 33 additions & 2 deletions posthog/src/main/java/com/posthog/PostHogConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,25 @@ public open class PostHogConfig(
* Defaults to null (evaluate all flags)
*/
public var evaluationEnvironments: List<String>? = null,
/**
* Automatically set common device and app properties as person properties for feature flag evaluation.
*
* When enabled, the SDK will automatically set the following person properties:
Copy link
Member

@marandaneto marandaneto Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should add $app_namespace as well
this is needed if i am running 2 apps within the same posthog project

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add $timezone?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in f917d11

* - $app_version: App version from package info
* - $app_build: App build number from package info
* - $os_name: Operating system name (Android)
* - $os_version: Operating system version
Comment on lines +84 to +85
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add $lib and $lib_version?
they are sent as headers (user agent) but its not used in flag evaluation i think
the problem is that you'd not be able to target a native mobile app or a site running on a mobile browser just with this information unless you pair up with something more eg $app_*
relates to PostHog/posthog#26274

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we decide to add those and the ones mentioned in the comment above, i'd suggest also adding to the iOS SDK https://github.com/PostHog/posthog-ios/blob/ba5dd65d2341ab8ea1949f072594ef67c490af3a/PostHog/PostHogConfig.swift#L98-L110

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an option, though it'd also require another change to set $lib and $lib_version as person properties (I suppose the same goes for $timezone and $locale as well). As of right now, there will be no way to select these properties while creating a new feature flag by default.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added these in f917d11. If this is something we want to support, we can keep them in - it does no harm to include them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mm, maybe adding something that isn't supported yet would just add confusion then?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe - I suppose I didn't update the JavaDoc for this to include these additional parameters (I'll do that now!), but to your point, it'd be confusing to see it documented but not usable. I'll pull out the non-default properties.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I do see these properties being sent in test data so maybe I'm wrong. I'll have to dig a little deeper.
https://github.com/PostHog/posthog-android/blob/06dd8aee2cee1e25c5e1a2ba97496af9b9c05e5c/posthog/src/test/resources/json/capture.json

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the end I ended up with $app_version, $app_build, $app_namespace, $os_name, $os_version, $device_type, $lib, $lib_version.

The others ($locale, $timezone, $device_manufacturer, $device_model) are not written to person properties.

* - $device_type: Device type (Mobile, Tablet, TV, etc.)
* - $device_manufacturer: Device manufacturer
* - $device_model: Device model
* - $locale: User's current locale
*
* This helps ensure feature flags that rely on these properties work correctly
* without waiting for server-side processing of identify() calls.
*
* Default: true
*/
public var setDefaultPersonProperties: Boolean = true,
/**
* Preload PostHog remote config automatically
* Defaults to true
Expand Down Expand Up @@ -192,8 +211,20 @@ public open class PostHogConfig(
/**
* Factory to instantiate a custom [com.posthog.internal.PostHogRemoteConfigInterface] implementation.
*/
public val remoteConfigProvider: (PostHogConfig, PostHogApi, ExecutorService) -> PostHogFeatureFlagsInterface =
{ config, api, executor -> PostHogRemoteConfig(config, api, executor) },
public val remoteConfigProvider: (
PostHogConfig,
PostHogApi,
ExecutorService,
(() -> Map<String, Any>)?,
) -> PostHogFeatureFlagsInterface =
{
config,
api,
executor,
getDefaultPersonProperties,
->
PostHogRemoteConfig(config, api, executor, getDefaultPersonProperties ?: { emptyMap() })
},
/**
* Factory to instantiate a custom queue implementation.
*/
Expand Down
2 changes: 1 addition & 1 deletion posthog/src/main/java/com/posthog/PostHogStateless.kt
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public open class PostHogStateless protected constructor(
config.storagePrefix,
queueExecutor,
)
val remoteConfig = config.remoteConfigProvider(config, api, featureFlagsExecutor)
val remoteConfig = config.remoteConfigProvider(config, api, featureFlagsExecutor, null)

// no need to lock optOut here since the setup is locked already
val optOut =
Expand Down
29 changes: 29 additions & 0 deletions posthog/src/main/java/com/posthog/internal/PostHogContext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,32 @@ public interface PostHogContext {

public fun getSdkInfo(): Map<String, Any>
}

/**
* Returns person properties context by extracting relevant properties from static context.
* This centralizes the logic for determining which properties should be used as person properties.
*/
@PostHogInternal
public fun PostHogContext.personPropertiesContext(): Map<String, Any> {
val staticCtx = getStaticContext()
val dynamicCtx = getDynamicContext()
val personProperties = mutableMapOf<String, Any>()

// App information
staticCtx["\$app_version"]?.let { personProperties["\$app_version"] = it }
staticCtx["\$app_build"]?.let { personProperties["\$app_build"] = it }

// Operating system information
staticCtx["\$os_name"]?.let { personProperties["\$os_name"] = it }
staticCtx["\$os_version"]?.let { personProperties["\$os_version"] = it }

// Device information
staticCtx["\$device_type"]?.let { personProperties["\$device_type"] = it }
staticCtx["\$device_manufacturer"]?.let { personProperties["\$device_manufacturer"] = it }
staticCtx["\$device_model"]?.let { personProperties["\$device_model"] = it }

// Localization (from dynamic context)
dynamicCtx["\$locale"]?.let { personProperties["\$locale"] = it }

return personProperties
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ internal class PostHogFlagsRequest(
this["\$anon_distinct_id"] = anonymousId
}
if (groups?.isNotEmpty() == true) {
this["\$groups"] = groups
this["groups"] = groups
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
if (personProperties?.isNotEmpty() == true) {
this["\$properties"] = personProperties
this["person_properties"] = personProperties
}
if (groupProperties?.isNotEmpty() == true) {
this["\$group_properties"] = groupProperties
this["group_properties"] = groupProperties
}
if (evaluationEnvironments?.isNotEmpty() == true) {
this["evaluation_environments"] = evaluationEnvironments
Expand Down
Loading
Loading