From 76c6a16b2121d076492cc4b71ae7823c296c75c1 Mon Sep 17 00:00:00 2001 From: Haven Date: Tue, 31 Mar 2026 11:43:22 -0500 Subject: [PATCH 01/18] feat(workflows): add push subscription functionality --- buildSrc/src/main/java/PosthogBuildConfig.kt | 2 + posthog-android/build.gradle.kts | 2 + .../android/PostHogPushNotifications.kt | 172 ++++++++++++++++++ posthog/src/main/java/com/posthog/PostHog.kt | 53 ++++++ .../main/java/com/posthog/PostHogInterface.kt | 15 ++ .../java/com/posthog/internal/PostHogApi.kt | 35 ++++ .../PostHogPushSubscriptionRequest.kt | 22 +++ 7 files changed, 301 insertions(+) create mode 100644 posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt create mode 100644 posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionRequest.kt diff --git a/buildSrc/src/main/java/PosthogBuildConfig.kt b/buildSrc/src/main/java/PosthogBuildConfig.kt index 6c0d22f3..b906329a 100644 --- a/buildSrc/src/main/java/PosthogBuildConfig.kt +++ b/buildSrc/src/main/java/PosthogBuildConfig.kt @@ -60,6 +60,8 @@ object PosthogBuildConfig { val CURTAINS = "1.2.5" val ANDROIDX_CORE = "1.5.0" val ANDROIDX_COMPOSE = "1.0.0" + val ANDROIDX_ACTIVITY = "1.7.2" + val FIREBASE_MESSAGING = "24.1.0" // tests val ANDROIDX_JUNIT = "1.2.1" diff --git a/posthog-android/build.gradle.kts b/posthog-android/build.gradle.kts index 6758858d..8786ed48 100644 --- a/posthog-android/build.gradle.kts +++ b/posthog-android/build.gradle.kts @@ -90,10 +90,12 @@ dependencies { implementation("androidx.lifecycle:lifecycle-process:${PosthogBuildConfig.Dependencies.LIFECYCLE}") implementation("androidx.lifecycle:lifecycle-common-java8:${PosthogBuildConfig.Dependencies.LIFECYCLE}") implementation("androidx.core:core:${PosthogBuildConfig.Dependencies.ANDROIDX_CORE}") + implementation("androidx.activity:activity-ktx:${PosthogBuildConfig.Dependencies.ANDROIDX_ACTIVITY}") implementation("com.squareup.curtains:curtains:${PosthogBuildConfig.Dependencies.CURTAINS}") // compile only compileOnly("androidx.compose.ui:ui:${PosthogBuildConfig.Dependencies.ANDROIDX_COMPOSE}") + compileOnly("com.google.firebase:firebase-messaging:${PosthogBuildConfig.Dependencies.FIREBASE_MESSAGING}") // compatibility signature("org.codehaus.mojo.signature:java18:${PosthogBuildConfig.Plugins.SIGNATURE_JAVA18}@signature") diff --git a/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt b/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt new file mode 100644 index 00000000..00feac02 --- /dev/null +++ b/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt @@ -0,0 +1,172 @@ +package com.posthog.android + +import android.Manifest +import android.app.Activity +import android.content.pm.PackageManager +import android.os.Build +import androidx.activity.ComponentActivity +import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.content.ContextCompat +import com.posthog.PostHog + +/** + * Utility class for handling push notification registration with PostHog. + * + * This class provides methods to request push notification permission and + * automatically register the device's FCM token with PostHog when permission is granted. + * + * Requirements: + * - Firebase Messaging must be included in the app's dependencies + * - Firebase must be initialized in the app + * + * Usage: + * ```kotlin + * // Call from a ComponentActivity (AppCompatActivity, FragmentActivity, etc.) + * PostHogPushNotifications.requestPermissionAndRegister(activity) + * ``` + */ +public object PostHogPushNotifications { + /** + * Requests push notification permission (on Android 13+) and automatically + * registers the FCM device token with PostHog when permission is granted. + * + * On Android 12 and below, notifications are allowed by default, so this method + * will directly proceed to register the FCM token. + * + * @param activity the ComponentActivity to use for the permission request. + * Must be a ComponentActivity (e.g. AppCompatActivity) to use the Activity Result API. + * @param onPermissionResult optional callback that receives the permission result. + * `true` if permission was granted (or not needed), `false` if denied. + */ + @JvmStatic + @JvmOverloads + public fun requestPermissionAndRegister( + activity: ComponentActivity, + onPermissionResult: ((Boolean) -> Unit)? = null, + ) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + val hasPermission = + ContextCompat.checkSelfPermission( + activity, + Manifest.permission.POST_NOTIFICATIONS, + ) == PackageManager.PERMISSION_GRANTED + + if (hasPermission) { + fetchAndRegisterToken(activity) + onPermissionResult?.invoke(true) + return + } + + val launcher = + activity.registerForActivityResult( + ActivityResultContracts.RequestPermission(), + ) { granted -> + if (granted) { + fetchAndRegisterToken(activity) + } + onPermissionResult?.invoke(granted) + } + + launcher.launch(Manifest.permission.POST_NOTIFICATIONS) + } else { + // Below Android 13, notification permission is granted at install time + fetchAndRegisterToken(activity) + onPermissionResult?.invoke(true) + } + } + + /** + * Checks whether push notification permission has already been granted. + * + * @param activity the Activity to check permission against + * @return true if permission is granted or the device is below Android 13 + */ + @JvmStatic + public fun hasPermission(activity: Activity): Boolean { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + ContextCompat.checkSelfPermission( + activity, + Manifest.permission.POST_NOTIFICATIONS, + ) == PackageManager.PERMISSION_GRANTED + } else { + true + } + } + + /** + * Registers a push notification token directly with PostHog without requesting permission. + * Use this if you already have the FCM token (e.g. from FirebaseMessagingService.onNewToken). + * + * @param token the FCM device token + * @param firebaseProjectId the Firebase project ID (used as appId for the push subscription) + */ + @JvmStatic + public fun registerToken( + token: String, + firebaseProjectId: String, + ) { + PostHog.registerPushNotificationToken( + token = token, + appId = firebaseProjectId, + platform = "android", + ) + } + + private fun fetchAndRegisterToken(activity: Activity) { + try { + val firebaseMessaging = + Class.forName("com.google.firebase.messaging.FirebaseMessaging") + + val getInstance = firebaseMessaging.getMethod("getInstance") + val instance = getInstance.invoke(null) + + val getToken = firebaseMessaging.getMethod("getToken") + val task = getToken.invoke(instance) + + // Get Firebase project ID + val firebaseApp = Class.forName("com.google.firebase.FirebaseApp") + val getFirebaseInstance = firebaseApp.getMethod("getInstance") + val appInstance = getFirebaseInstance.invoke(null) + val getOptions = firebaseApp.getMethod("getOptions") + val options = getOptions.invoke(appInstance) + + val firebaseOptions = Class.forName("com.google.firebase.FirebaseOptions") + val getProjectId = firebaseOptions.getMethod("getProjectId") + val projectId = getProjectId.invoke(options) as? String ?: "" + + // task is a com.google.android.gms.tasks.Task + val taskClass = Class.forName("com.google.android.gms.tasks.Task") + val addOnSuccessListenerMethod = + taskClass.getMethod( + "addOnSuccessListener", + Class.forName("com.google.android.gms.tasks.OnSuccessListener"), + ) + + val proxy = + java.lang.reflect.Proxy.newProxyInstance( + activity.classLoader, + arrayOf(Class.forName("com.google.android.gms.tasks.OnSuccessListener")), + ) { _, _, args -> + val token = args?.firstOrNull() as? String + if (!token.isNullOrBlank() && projectId.isNotBlank()) { + PostHog.registerPushNotificationToken( + token = token, + appId = projectId, + platform = "android", + ) + } + null + } + + addOnSuccessListenerMethod.invoke(task, proxy) + } catch (e: ClassNotFoundException) { + val config = PostHog.getConfig() + config?.logger?.log( + "Firebase Messaging is not available. Add firebase-messaging dependency to use push notifications.", + ) + } catch (e: Throwable) { + val config = PostHog.getConfig() + config?.logger?.log("Failed to fetch FCM token: $e.") + } + } +} diff --git a/posthog/src/main/java/com/posthog/PostHog.kt b/posthog/src/main/java/com/posthog/PostHog.kt index 690e144f..de5712c8 100644 --- a/posthog/src/main/java/com/posthog/PostHog.kt +++ b/posthog/src/main/java/com/posthog/PostHog.kt @@ -61,6 +61,7 @@ public class PostHog private constructor( private val cachedPersonPropertiesLock = Any() private var replayQueue: PostHogQueueInterface? = null + private var api: PostHogApi? = null private val remoteConfig: PostHogRemoteConfig? get() = config?.remoteConfigHolder @@ -163,6 +164,7 @@ public class PostHog private constructor( ) this.config = config + this.api = api this.queue = queue this.replayQueue = replayQueue @@ -294,6 +296,7 @@ public class PostHog private constructor( queue?.stop() replayQueue?.stop() + api = null featureFlagsCalled.clear() @@ -1307,6 +1310,48 @@ public class PostHog private constructor( return PostHogSessionManager.isSessionActive() } + override fun registerPushNotificationToken( + token: String, + appId: String, + platform: String, + ) { + if (!isEnabled()) { + return + } + if (config?.optOut == true) { + config?.logger?.log("PostHog is in OptOut state.") + return + } + if (token.isBlank()) { + config?.logger?.log("registerPushNotificationToken call not allowed, token is blank.") + return + } + if (appId.isBlank()) { + config?.logger?.log("registerPushNotificationToken call not allowed, appId is blank.") + return + } + + val currentDistinctId = distinctId + if (currentDistinctId.isBlank()) { + config?.logger?.log("registerPushNotificationToken call not allowed, distinctId is invalid.") + return + } + + queueExecutor.execute { + try { + api?.pushSubscription( + distinctId = currentDistinctId, + token = token, + platform = platform, + appId = appId, + ) + config?.logger?.log("Push notification token registered successfully.") + } catch (e: Throwable) { + config?.logger?.log("Failed to register push notification token: $e.") + } + } + } + override fun getConfig(): T? { @Suppress("UNCHECKED_CAST") return super.config as? T @@ -1662,5 +1707,13 @@ public class PostHog private constructor( override fun getSessionId(): UUID? { return shared.getSessionId() } + + override fun registerPushNotificationToken( + token: String, + appId: String, + platform: String, + ) { + shared.registerPushNotificationToken(token, appId, platform) + } } } diff --git a/posthog/src/main/java/com/posthog/PostHogInterface.kt b/posthog/src/main/java/com/posthog/PostHogInterface.kt index 5f3d4522..ffce4e04 100644 --- a/posthog/src/main/java/com/posthog/PostHogInterface.kt +++ b/posthog/src/main/java/com/posthog/PostHogInterface.kt @@ -284,6 +284,21 @@ public interface PostHogInterface : PostHogCoreInterface { flagVariant: String? = null, ) + /** + * Registers a push notification device token with PostHog. + * This sends the token to the PostHog push subscriptions API so that + * push notifications can be delivered to this device. + * + * @param token the device push token (e.g. FCM registration token) + * @param appId the app identifier - Firebase project_id for Android, APNS bundle_id for iOS + * @param platform the platform, defaults to "android" + */ + public fun registerPushNotificationToken( + token: String, + appId: String, + platform: String = "android", + ) + @PostHogInternal public fun getConfig(): T? } diff --git a/posthog/src/main/java/com/posthog/internal/PostHogApi.kt b/posthog/src/main/java/com/posthog/internal/PostHogApi.kt index c96e8e90..22ecc00e 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogApi.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogApi.kt @@ -275,6 +275,41 @@ public class PostHogApi( } } + @Throws(PostHogApiError::class, IOException::class) + public fun pushSubscription( + distinctId: String, + token: String, + platform: String, + appId: String, + ) { + val request = + PostHogPushSubscriptionRequest( + apiKey = config.apiKey, + distinctId = distinctId, + token = token, + platform = platform, + appId = appId, + ) + + val url = "$theHost/api/push_subscriptions/" + logRequest(request, url) + + val httpRequest = + makeRequest(url) { + config.serializer.serialize(request, it.bufferedWriter()) + } + + logRequestHeaders(httpRequest) + + client.newCall(httpRequest).execute().use { + val response = logResponse(it) + + if (!response.isSuccessful) { + throw PostHogApiError(response.code, response.message, response.body) + } + } + } + private fun logResponse(response: Response): Response { if (config.debug) { try { diff --git a/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionRequest.kt b/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionRequest.kt new file mode 100644 index 00000000..eb5a2b31 --- /dev/null +++ b/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionRequest.kt @@ -0,0 +1,22 @@ +package com.posthog.internal + +import com.google.gson.annotations.SerializedName + +/** + * The request body for the push subscriptions API + * @property apiKey the PostHog API Key + * @property distinctId the user's distinct ID + * @property token the device push token (FCM or APNS) + * @property platform the platform ("android" or "ios") + * @property appId the Firebase project_id (for Android) or APNS bundle_id (for iOS) + */ +internal data class PostHogPushSubscriptionRequest( + @SerializedName("api_key") + val apiKey: String, + @SerializedName("distinct_id") + val distinctId: String, + val token: String, + val platform: String, + @SerializedName("app_id") + val appId: String, +) From 232059d02ab63ee6afaf6c101ffc9b5a700dc26c Mon Sep 17 00:00:00 2001 From: Haven Date: Thu, 2 Apr 2026 12:12:53 -0500 Subject: [PATCH 02/18] update token->deviceToken --- .../com/posthog/android/PostHogPushNotifications.kt | 8 ++++---- posthog/src/main/java/com/posthog/PostHog.kt | 12 ++++++------ .../src/main/java/com/posthog/PostHogInterface.kt | 4 ++-- .../src/main/java/com/posthog/internal/PostHogApi.kt | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt b/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt index 00feac02..11502291 100644 --- a/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt +++ b/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt @@ -97,16 +97,16 @@ public object PostHogPushNotifications { * Registers a push notification token directly with PostHog without requesting permission. * Use this if you already have the FCM token (e.g. from FirebaseMessagingService.onNewToken). * - * @param token the FCM device token + * @param deviceToken the FCM device token * @param firebaseProjectId the Firebase project ID (used as appId for the push subscription) */ @JvmStatic public fun registerToken( - token: String, + deviceToken: String, firebaseProjectId: String, ) { PostHog.registerPushNotificationToken( - token = token, + deviceToken = deviceToken, appId = firebaseProjectId, platform = "android", ) @@ -150,7 +150,7 @@ public object PostHogPushNotifications { val token = args?.firstOrNull() as? String if (!token.isNullOrBlank() && projectId.isNotBlank()) { PostHog.registerPushNotificationToken( - token = token, + deviceToken = deviceToken, appId = projectId, platform = "android", ) diff --git a/posthog/src/main/java/com/posthog/PostHog.kt b/posthog/src/main/java/com/posthog/PostHog.kt index de5712c8..26b596ff 100644 --- a/posthog/src/main/java/com/posthog/PostHog.kt +++ b/posthog/src/main/java/com/posthog/PostHog.kt @@ -1311,7 +1311,7 @@ public class PostHog private constructor( } override fun registerPushNotificationToken( - token: String, + deviceToken: String, appId: String, platform: String, ) { @@ -1322,8 +1322,8 @@ public class PostHog private constructor( config?.logger?.log("PostHog is in OptOut state.") return } - if (token.isBlank()) { - config?.logger?.log("registerPushNotificationToken call not allowed, token is blank.") + if (deviceToken.isBlank()) { + config?.logger?.log("registerPushNotificationToken call not allowed, deviceToken is blank.") return } if (appId.isBlank()) { @@ -1341,7 +1341,7 @@ public class PostHog private constructor( try { api?.pushSubscription( distinctId = currentDistinctId, - token = token, + deviceToken = deviceToken, platform = platform, appId = appId, ) @@ -1709,11 +1709,11 @@ public class PostHog private constructor( } override fun registerPushNotificationToken( - token: String, + deviceToken: String, appId: String, platform: String, ) { - shared.registerPushNotificationToken(token, appId, platform) + shared.registerPushNotificationToken(deviceToken, appId, platform) } } } diff --git a/posthog/src/main/java/com/posthog/PostHogInterface.kt b/posthog/src/main/java/com/posthog/PostHogInterface.kt index ffce4e04..a10f8678 100644 --- a/posthog/src/main/java/com/posthog/PostHogInterface.kt +++ b/posthog/src/main/java/com/posthog/PostHogInterface.kt @@ -289,12 +289,12 @@ public interface PostHogInterface : PostHogCoreInterface { * This sends the token to the PostHog push subscriptions API so that * push notifications can be delivered to this device. * - * @param token the device push token (e.g. FCM registration token) + * @param deviceToken the device push token (e.g. FCM registration token) * @param appId the app identifier - Firebase project_id for Android, APNS bundle_id for iOS * @param platform the platform, defaults to "android" */ public fun registerPushNotificationToken( - token: String, + deviceToken: String, appId: String, platform: String = "android", ) diff --git a/posthog/src/main/java/com/posthog/internal/PostHogApi.kt b/posthog/src/main/java/com/posthog/internal/PostHogApi.kt index 22ecc00e..f31572be 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogApi.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogApi.kt @@ -278,7 +278,7 @@ public class PostHogApi( @Throws(PostHogApiError::class, IOException::class) public fun pushSubscription( distinctId: String, - token: String, + deviceToken: String, platform: String, appId: String, ) { @@ -286,7 +286,7 @@ public class PostHogApi( PostHogPushSubscriptionRequest( apiKey = config.apiKey, distinctId = distinctId, - token = token, + deviceToken = deviceToken, platform = platform, appId = appId, ) From c55a7d29743043b5a82f62a313a22920869f79ce Mon Sep 17 00:00:00 2001 From: Haven Date: Tue, 21 Apr 2026 12:50:18 -0500 Subject: [PATCH 03/18] add offline/retry support for notification subscription --- .../android/PostHogPushNotifications.kt | 2 +- posthog/src/main/java/com/posthog/PostHog.kt | 25 +- .../PostHogPushSubscriptionManager.kt | 225 ++++++++++++++++++ .../PostHogPushSubscriptionRequest.kt | 5 +- 4 files changed, 241 insertions(+), 16 deletions(-) create mode 100644 posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionManager.kt diff --git a/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt b/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt index 11502291..590bb096 100644 --- a/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt +++ b/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt @@ -150,7 +150,7 @@ public object PostHogPushNotifications { val token = args?.firstOrNull() as? String if (!token.isNullOrBlank() && projectId.isNotBlank()) { PostHog.registerPushNotificationToken( - deviceToken = deviceToken, + deviceToken = token, appId = projectId, platform = "android", ) diff --git a/posthog/src/main/java/com/posthog/PostHog.kt b/posthog/src/main/java/com/posthog/PostHog.kt index 95866217..a1dd5ff1 100644 --- a/posthog/src/main/java/com/posthog/PostHog.kt +++ b/posthog/src/main/java/com/posthog/PostHog.kt @@ -18,6 +18,7 @@ import com.posthog.internal.PostHogPreferences.Companion.OPT_OUT import com.posthog.internal.PostHogPreferences.Companion.PERSON_PROCESSING import com.posthog.internal.PostHogPreferences.Companion.VERSION import com.posthog.internal.PostHogPrintLogger +import com.posthog.internal.PostHogPushSubscriptionManager import com.posthog.internal.PostHogQueueInterface import com.posthog.internal.PostHogRemoteConfig import com.posthog.internal.PostHogSendCachedEventsIntegration @@ -64,6 +65,7 @@ public class PostHog private constructor( private var replayQueue: PostHogQueueInterface? = null private var api: PostHogApi? = null + private var pushSubscriptionManager: PostHogPushSubscriptionManager? = null private val remoteConfig: PostHogRemoteConfig? get() = config?.remoteConfigHolder @@ -173,6 +175,7 @@ public class PostHog private constructor( this.api = api this.queue = queue this.replayQueue = replayQueue + this.pushSubscriptionManager = PostHogPushSubscriptionManager(config, api, queueExecutor) if (featureFlags is PostHogRemoteConfig) { config.remoteConfigHolder = featureFlags @@ -192,6 +195,8 @@ public class PostHog private constructor( queue.start() + pushSubscriptionManager?.retryPending() + startSession() config.integrations.forEach { @@ -308,6 +313,7 @@ public class PostHog private constructor( queue?.stop() replayQueue?.stop() api = null + pushSubscriptionManager = null featureFlagsCalled.clear() @@ -1383,19 +1389,12 @@ public class PostHog private constructor( return } - queueExecutor.execute { - try { - api?.pushSubscription( - distinctId = currentDistinctId, - deviceToken = deviceToken, - platform = platform, - appId = appId, - ) - config?.logger?.log("Push notification token registered successfully.") - } catch (e: Throwable) { - config?.logger?.log("Failed to register push notification token: $e.") - } - } + pushSubscriptionManager?.register( + distinctId = currentDistinctId, + deviceToken = deviceToken, + appId = appId, + platform = platform, + ) } override fun getConfig(): T? { diff --git a/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionManager.kt b/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionManager.kt new file mode 100644 index 00000000..e42aa304 --- /dev/null +++ b/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionManager.kt @@ -0,0 +1,225 @@ +package com.posthog.internal + +import com.google.gson.annotations.SerializedName +import com.posthog.PostHogConfig +import java.io.File +import java.io.IOException +import java.util.Timer +import java.util.concurrent.ExecutorService +import java.util.concurrent.atomic.AtomicBoolean +import kotlin.concurrent.schedule +import kotlin.math.min +import kotlin.math.pow + +private const val PENDING_FILE_NAME = "push_subscription.pending" +private val RETRYABLE_STATUS_CODES = setOf(429, 500, 502, 503, 504) + +/** + * Persists the latest push subscription registration to disk and retries it on + * transient failures. The wire call is a single POST, so events here are + * latest-wins: each new register() overwrites any pending record. + * + * Recovery across process restarts is driven by [retryPending], which the SDK + * invokes once after setup completes. + */ +internal class PostHogPushSubscriptionManager( + private val config: PostHogConfig, + private val api: PostHogApi, + private val executor: ExecutorService, +) { + private val timerLock = Any() + private val isSending = AtomicBoolean(false) + + @Volatile private var retryCount = 0 + + @Volatile private var timer: Timer? = null + + private val initialRetryDelaySeconds = 1 + private val maxRetryDelaySeconds = 30 + + fun register( + distinctId: String, + deviceToken: String, + appId: String, + platform: String, + ) { + val record = PendingRecord(distinctId, deviceToken, appId, platform) + executor.executeSafely { + val file = pendingFile() + if (file != null) { + writeRecord(file, record) + } + retryCount = 0 + cancelTimer() + attempt(record, file) + } + } + + fun retryPending() { + val file = pendingFile() ?: return + executor.executeSafely { + if (!file.existsSafely(config)) { + return@executeSafely + } + val record = readRecord(file) ?: run { + file.deleteSafely(config) + return@executeSafely + } + retryCount = 0 + cancelTimer() + attempt(record, file) + } + } + + private fun attempt( + record: PendingRecord, + file: File?, + ) { + if (config.networkStatus?.isConnected() == false) { + config.logger.log("Push subscription deferred: no network.") + return + } + + if (!isSending.compareAndSet(false, true)) { + return + } + + try { + api.pushSubscription( + distinctId = record.distinctId, + deviceToken = record.deviceToken, + platform = record.platform, + appId = record.appId, + ) + config.logger.log("Push notification token registered successfully.") + retryCount = 0 + // Latest-wins: only delete if the on-disk record hasn't been replaced + // by a concurrent register() call. + if (file != null && readRecord(file) == record) { + file.deleteSafely(config) + } + } catch (e: Throwable) { + handleFailure(e, record, file) + } finally { + isSending.set(false) + } + } + + private fun handleFailure( + e: Throwable, + record: PendingRecord, + file: File?, + ) { + if (!isRetryable(e)) { + config.logger.log("Push subscription failed with non-retryable error: $e.") + file?.deleteSafely(config) + retryCount = 0 + return + } + + retryCount++ + if (retryCount > config.maxRetries) { + config.logger.log( + "Push subscription retries exhausted after $retryCount attempts; " + + "will retry on next SDK startup.", + ) + retryCount = 0 + return + } + + val retryAfter = (e as? PostHogApiError)?.retryAfterSeconds + val delay = + if (retryAfter != null && retryAfter > 0) { + retryAfter + } else { + min( + initialRetryDelaySeconds * 2.0.pow((retryCount - 1).toDouble()).toInt(), + maxRetryDelaySeconds, + ) + } + config.logger.log("Push subscription failed: $e. Retrying in ${delay}s (attempt $retryCount).") + scheduleRetry(delay, record, file) + } + + private fun scheduleRetry( + delaySeconds: Int, + record: PendingRecord, + file: File?, + ) { + synchronized(timerLock) { + cancelTimer() + val t = Timer(true) + t.schedule(delaySeconds * 1000L) { + executor.executeSafely { + // Re-read from disk in case the record was replaced while we waited. + val current = file?.let { readRecord(it) } ?: record + attempt(current, file) + } + } + timer = t + } + } + + private fun cancelTimer() { + synchronized(timerLock) { + timer?.cancel() + timer = null + } + } + + private fun isRetryable(e: Throwable): Boolean { + return when (e) { + is PostHogApiError -> e.statusCode < 400 || e.statusCode in RETRYABLE_STATUS_CODES + is IOException -> true + else -> false + } + } + + private fun pendingFile(): File? { + val prefix = config.storagePrefix ?: return null + val dir = File(prefix, config.apiKey) + try { + dir.mkdirs() + } catch (e: Throwable) { + config.logger.log("Failed to create push subscription dir: $e.") + return null + } + return File(dir, PENDING_FILE_NAME) + } + + private fun writeRecord( + file: File, + record: PendingRecord, + ) { + try { + val os = config.encryption?.encrypt(file.outputStream()) ?: file.outputStream() + os.use { theOutputStream -> + config.serializer.serialize(record, theOutputStream.writer().buffered()) + } + } catch (e: Throwable) { + config.logger.log("Failed to persist push subscription: $e.") + } + } + + private fun readRecord(file: File): PendingRecord? { + return try { + val input = config.encryption?.decrypt(file.inputStream()) ?: file.inputStream() + input.use { + config.serializer.deserialize(it.reader().buffered()) + } + } catch (e: Throwable) { + config.logger.log("Failed to read pending push subscription: $e.") + null + } + } + + internal data class PendingRecord( + @SerializedName("distinct_id") + val distinctId: String, + @SerializedName("device_token") + val deviceToken: String, + @SerializedName("app_id") + val appId: String, + val platform: String, + ) +} diff --git a/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionRequest.kt b/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionRequest.kt index eb5a2b31..4c54633b 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionRequest.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionRequest.kt @@ -6,7 +6,7 @@ import com.google.gson.annotations.SerializedName * The request body for the push subscriptions API * @property apiKey the PostHog API Key * @property distinctId the user's distinct ID - * @property token the device push token (FCM or APNS) + * @property deviceToken the device push token (FCM or APNS) * @property platform the platform ("android" or "ios") * @property appId the Firebase project_id (for Android) or APNS bundle_id (for iOS) */ @@ -15,7 +15,8 @@ internal data class PostHogPushSubscriptionRequest( val apiKey: String, @SerializedName("distinct_id") val distinctId: String, - val token: String, + @SerializedName("token") + val deviceToken: String, val platform: String, @SerializedName("app_id") val appId: String, From d3a781579bc72b6200e91f1be03678528071595f Mon Sep 17 00:00:00 2001 From: Haven Date: Wed, 22 Apr 2026 14:31:05 -0500 Subject: [PATCH 04/18] fix request body --- .../google-services.json | 29 +++++++++++++++++++ .../PostHogPushSubscriptionRequest.kt | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 posthog-samples/posthog-android-sample/google-services.json diff --git a/posthog-samples/posthog-android-sample/google-services.json b/posthog-samples/posthog-android-sample/google-services.json new file mode 100644 index 00000000..ce7dc624 --- /dev/null +++ b/posthog-samples/posthog-android-sample/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "377685470810", + "project_id": "hackier-news", + "storage_bucket": "hackier-news.firebasestorage.app" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:377685470810:android:14b6a082e22d8078cb3263", + "android_client_info": { + "package_name": "com.posthog.android.sample" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyBoVQSXb2Oe4Ruagi9kat0Ib9A-_yJVoo8" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionRequest.kt b/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionRequest.kt index 4c54633b..ddec38c2 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionRequest.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionRequest.kt @@ -15,7 +15,7 @@ internal data class PostHogPushSubscriptionRequest( val apiKey: String, @SerializedName("distinct_id") val distinctId: String, - @SerializedName("token") + @SerializedName("device_token") val deviceToken: String, val platform: String, @SerializedName("app_id") From a64133a9fa37b5728a6800e7504db77883e4c692 Mon Sep 17 00:00:00 2001 From: Haven Date: Wed, 22 Apr 2026 14:39:13 -0500 Subject: [PATCH 05/18] tweak --- .../google-services.json | 29 ------------------- 1 file changed, 29 deletions(-) delete mode 100644 posthog-samples/posthog-android-sample/google-services.json diff --git a/posthog-samples/posthog-android-sample/google-services.json b/posthog-samples/posthog-android-sample/google-services.json deleted file mode 100644 index ce7dc624..00000000 --- a/posthog-samples/posthog-android-sample/google-services.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "project_info": { - "project_number": "377685470810", - "project_id": "hackier-news", - "storage_bucket": "hackier-news.firebasestorage.app" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:377685470810:android:14b6a082e22d8078cb3263", - "android_client_info": { - "package_name": "com.posthog.android.sample" - } - }, - "oauth_client": [], - "api_key": [ - { - "current_key": "AIzaSyBoVQSXb2Oe4Ruagi9kat0Ib9A-_yJVoo8" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [] - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file From d0117b6625755cc74dafdc07968629d24b738116 Mon Sep 17 00:00:00 2001 From: Haven Date: Wed, 22 Apr 2026 14:45:36 -0500 Subject: [PATCH 06/18] tweak --- .../android/PostHogPushNotifications.kt | 94 +++++++++++-------- 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt b/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt index 590bb096..4a0cac65 100644 --- a/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt +++ b/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt @@ -5,74 +5,88 @@ import android.app.Activity import android.content.pm.PackageManager import android.os.Build import androidx.activity.ComponentActivity +import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts import androidx.core.content.ContextCompat import com.posthog.PostHog +/** + * Wraps an [ActivityResultLauncher] for the POST_NOTIFICATIONS permission and ties + * its result to FCM token registration with PostHog. Created by + * [PostHogPushNotifications.registerPermissionLauncher]; safe to invoke [launch] from + * any lifecycle state (e.g. button handlers) after the owning activity is created. + */ +public class PostHogPushPermissionLauncher internal constructor( + private val activity: Activity, + private val permissionLauncher: ActivityResultLauncher?, +) { + /** + * If permission is already granted (or not required), fetches the FCM token and + * registers it with PostHog immediately. Otherwise, prompts the user for + * POST_NOTIFICATIONS; on grant, the token is fetched and registered. + */ + public fun launch() { + if (PostHogPushNotifications.hasPermission(activity)) { + PostHogPushNotifications.fetchAndRegisterFcmToken(activity) + return + } + permissionLauncher?.launch(Manifest.permission.POST_NOTIFICATIONS) + } +} + /** * Utility class for handling push notification registration with PostHog. * - * This class provides methods to request push notification permission and - * automatically register the device's FCM token with PostHog when permission is granted. - * * Requirements: * - Firebase Messaging must be included in the app's dependencies * - Firebase must be initialized in the app * * Usage: * ```kotlin - * // Call from a ComponentActivity (AppCompatActivity, FragmentActivity, etc.) - * PostHogPushNotifications.requestPermissionAndRegister(activity) + * class MyActivity : ComponentActivity() { + * // Must be created before the activity reaches STARTED — a field initializer + * // or onCreate is fine. Do NOT call this from a button handler. + * private val pushLauncher = PostHogPushNotifications.registerPermissionLauncher(this) + * + * override fun onCreate(savedInstanceState: Bundle?) { + * super.onCreate(savedInstanceState) + * // Invoke from anywhere — button click, settings toggle, first launch, etc. + * pushLauncher.launch() + * } + * } * ``` */ public object PostHogPushNotifications { /** - * Requests push notification permission (on Android 13+) and automatically - * registers the FCM device token with PostHog when permission is granted. + * Registers an Activity Result launcher for the POST_NOTIFICATIONS permission. + * Must be called before the activity reaches STARTED (field initializer or onCreate). + * The returned [PostHogPushPermissionLauncher] can be invoked from any lifecycle + * state to prompt for permission (if needed) and register the FCM token. * - * On Android 12 and below, notifications are allowed by default, so this method - * will directly proceed to register the FCM token. - * - * @param activity the ComponentActivity to use for the permission request. - * Must be a ComponentActivity (e.g. AppCompatActivity) to use the Activity Result API. - * @param onPermissionResult optional callback that receives the permission result. - * `true` if permission was granted (or not needed), `false` if denied. + * @param activity the ComponentActivity that owns the launcher + * @param onPermissionResult optional callback; receives `true` if permission was + * granted (or not required), `false` if denied */ @JvmStatic @JvmOverloads - public fun requestPermissionAndRegister( + public fun registerPermissionLauncher( activity: ComponentActivity, onPermissionResult: ((Boolean) -> Unit)? = null, - ) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - val hasPermission = - ContextCompat.checkSelfPermission( - activity, - Manifest.permission.POST_NOTIFICATIONS, - ) == PackageManager.PERMISSION_GRANTED - - if (hasPermission) { - fetchAndRegisterToken(activity) - onPermissionResult?.invoke(true) - return - } - - val launcher = + ): PostHogPushPermissionLauncher { + val launcher = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { activity.registerForActivityResult( ActivityResultContracts.RequestPermission(), ) { granted -> if (granted) { - fetchAndRegisterToken(activity) + fetchAndRegisterFcmToken(activity) } onPermissionResult?.invoke(granted) } - - launcher.launch(Manifest.permission.POST_NOTIFICATIONS) - } else { - // Below Android 13, notification permission is granted at install time - fetchAndRegisterToken(activity) - onPermissionResult?.invoke(true) - } + } else { + null + } + return PostHogPushPermissionLauncher(activity, launcher) } /** @@ -112,7 +126,7 @@ public object PostHogPushNotifications { ) } - private fun fetchAndRegisterToken(activity: Activity) { + internal fun fetchAndRegisterFcmToken(activity: Activity) { try { val firebaseMessaging = Class.forName("com.google.firebase.messaging.FirebaseMessaging") @@ -123,7 +137,6 @@ public object PostHogPushNotifications { val getToken = firebaseMessaging.getMethod("getToken") val task = getToken.invoke(instance) - // Get Firebase project ID val firebaseApp = Class.forName("com.google.firebase.FirebaseApp") val getFirebaseInstance = firebaseApp.getMethod("getInstance") val appInstance = getFirebaseInstance.invoke(null) @@ -134,7 +147,6 @@ public object PostHogPushNotifications { val getProjectId = firebaseOptions.getMethod("getProjectId") val projectId = getProjectId.invoke(options) as? String ?: "" - // task is a com.google.android.gms.tasks.Task val taskClass = Class.forName("com.google.android.gms.tasks.Task") val addOnSuccessListenerMethod = taskClass.getMethod( From 5046a96e6d6361a59a0bd42d46ed76f7d2b1dcb6 Mon Sep 17 00:00:00 2001 From: Haven Date: Wed, 22 Apr 2026 17:45:30 -0500 Subject: [PATCH 07/18] add tests --- .../java/com/posthog/android/PostHogFake.kt | 15 ++ .../android/PostHogPushNotificationsTest.kt | 38 +++ .../src/test/java/com/posthog/PostHogTest.kt | 86 +++++++ .../com/posthog/internal/PostHogApiTest.kt | 50 ++++ .../PostHogPushSubscriptionManagerTest.kt | 234 ++++++++++++++++++ 5 files changed, 423 insertions(+) create mode 100644 posthog-android/src/test/java/com/posthog/android/PostHogPushNotificationsTest.kt create mode 100644 posthog/src/test/java/com/posthog/internal/PostHogPushSubscriptionManagerTest.kt diff --git a/posthog-android/src/test/java/com/posthog/android/PostHogFake.kt b/posthog-android/src/test/java/com/posthog/android/PostHogFake.kt index 1960d808..25023f6b 100644 --- a/posthog-android/src/test/java/com/posthog/android/PostHogFake.kt +++ b/posthog-android/src/test/java/com/posthog/android/PostHogFake.kt @@ -13,6 +13,10 @@ public class PostHogFake : PostHogInterface { public var properties: Map? = null public var captures: Int = 0 public var flushes: Int = 0 + public var pushDeviceToken: String? = null + public var pushAppId: String? = null + public var pushPlatform: String? = null + public var pushRegistrations: Int = 0 override fun setup(config: T) { } @@ -202,6 +206,17 @@ public class PostHogFake : PostHogInterface { return null } + override fun registerPushNotificationToken( + deviceToken: String, + appId: String, + platform: String, + ) { + pushDeviceToken = deviceToken + pushAppId = appId + pushPlatform = platform + pushRegistrations++ + } + override fun getConfig(): T? { return null } diff --git a/posthog-android/src/test/java/com/posthog/android/PostHogPushNotificationsTest.kt b/posthog-android/src/test/java/com/posthog/android/PostHogPushNotificationsTest.kt new file mode 100644 index 00000000..c6762e4e --- /dev/null +++ b/posthog-android/src/test/java/com/posthog/android/PostHogPushNotificationsTest.kt @@ -0,0 +1,38 @@ +package com.posthog.android + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.posthog.PostHog +import org.junit.runner.RunWith +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +@RunWith(AndroidJUnit4::class) +internal class PostHogPushNotificationsTest { + private lateinit var fake: PostHogFake + + @BeforeTest + fun `set up`() { + PostHog.close() + fake = createPostHogFake() + } + + @AfterTest + fun `set down`() { + PostHog.resetSharedInstance() + } + + @Test + fun `registerToken delegates to PostHog with android platform`() { + PostHogPushNotifications.registerToken( + deviceToken = "fcm-token", + firebaseProjectId = "firebase-project", + ) + + assertEquals(1, fake.pushRegistrations) + assertEquals("fcm-token", fake.pushDeviceToken) + assertEquals("firebase-project", fake.pushAppId) + assertEquals("android", fake.pushPlatform) + } +} diff --git a/posthog/src/test/java/com/posthog/PostHogTest.kt b/posthog/src/test/java/com/posthog/PostHogTest.kt index 79b6052e..b94a93b3 100644 --- a/posthog/src/test/java/com/posthog/PostHogTest.kt +++ b/posthog/src/test/java/com/posthog/PostHogTest.kt @@ -3222,4 +3222,90 @@ internal class PostHogTest { sut.close() } + + @Test + fun `registerPushNotificationToken posts to push_subscriptions endpoint`() { + val http = mockHttp() + val url = http.url("/") + + val sut = getSut(url.toString(), preloadFeatureFlags = false, reloadFeatureFlags = false) + + sut.registerPushNotificationToken( + deviceToken = "fcm-token", + appId = "firebase-project", + platform = "android", + ) + + queueExecutor.awaitExecution() + + val request = http.takeRequest() + assertEquals("POST", request.method) + assertEquals("/api/push_subscriptions/", request.path) + + val parsed = serializer.deserialize>(request.body.unGzip().reader()) + assertEquals("fcm-token", parsed["device_token"]) + assertEquals("firebase-project", parsed["app_id"]) + assertEquals("android", parsed["platform"]) + assertEquals(sut.distinctId(), parsed["distinct_id"]) + + sut.close() + } + + @Test + fun `registerPushNotificationToken is a no-op when optOut`() { + val http = mockHttp() + val url = http.url("/") + + val sut = getSut(url.toString(), optOut = true, preloadFeatureFlags = false, reloadFeatureFlags = false) + + sut.registerPushNotificationToken("fcm-token", "firebase-project", "android") + queueExecutor.awaitExecution() + + assertEquals(0, http.requestCount) + + sut.close() + } + + @Test + fun `registerPushNotificationToken is a no-op when deviceToken is blank`() { + val http = mockHttp() + val url = http.url("/") + + val sut = getSut(url.toString(), preloadFeatureFlags = false, reloadFeatureFlags = false) + + sut.registerPushNotificationToken(" ", "firebase-project", "android") + queueExecutor.awaitExecution() + + assertEquals(0, http.requestCount) + + sut.close() + } + + @Test + fun `registerPushNotificationToken is a no-op when appId is blank`() { + val http = mockHttp() + val url = http.url("/") + + val sut = getSut(url.toString(), preloadFeatureFlags = false, reloadFeatureFlags = false) + + sut.registerPushNotificationToken("fcm-token", "", "android") + queueExecutor.awaitExecution() + + assertEquals(0, http.requestCount) + + sut.close() + } + + @Test + fun `registerPushNotificationToken after close does not crash and sends nothing`() { + val http = mockHttp() + val url = http.url("/") + + val sut = getSut(url.toString(), preloadFeatureFlags = false, reloadFeatureFlags = false) + sut.close() + + sut.registerPushNotificationToken("fcm-token", "firebase-project", "android") + + assertEquals(0, http.requestCount) + } } diff --git a/posthog/src/test/java/com/posthog/internal/PostHogApiTest.kt b/posthog/src/test/java/com/posthog/internal/PostHogApiTest.kt index d68c238e..2ec53f6d 100644 --- a/posthog/src/test/java/com/posthog/internal/PostHogApiTest.kt +++ b/posthog/src/test/java/com/posthog/internal/PostHogApiTest.kt @@ -5,6 +5,7 @@ import com.posthog.BuildConfig import com.posthog.PostHogConfig import com.posthog.generateEvent import com.posthog.mockHttp +import com.posthog.unGzip import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer import org.junit.Assert.assertThrows @@ -417,6 +418,55 @@ internal class PostHogApiTest { ) } + @Test + fun `pushSubscription posts request with expected body and path`() { + val http = mockHttp() + val url = http.url("/") + + val sut = getSut(host = url.toString()) + + sut.pushSubscription( + distinctId = "distinctId", + deviceToken = "fcm-token-123", + platform = "android", + appId = "firebase-project-id", + ) + + val request = http.takeRequest() + + assertEquals("POST", request.method) + assertEquals("/api/push_subscriptions/", request.path) + assertEquals("gzip", request.headers["Content-Encoding"]) + assertEquals("application/json; charset=utf-8", request.headers["Content-Type"]) + + val body = request.body.unGzip() + val parsed = PostHogSerializer(PostHogConfig(API_KEY)).deserialize>(body.reader()) + assertEquals(API_KEY, parsed["api_key"]) + assertEquals("distinctId", parsed["distinct_id"]) + assertEquals("fcm-token-123", parsed["device_token"]) + assertEquals("android", parsed["platform"]) + assertEquals("firebase-project-id", parsed["app_id"]) + } + + @Test + fun `pushSubscription throws if not successful`() { + val http = mockHttp(response = MockResponse().setResponseCode(400).setBody("error")) + val url = http.url("/") + + val sut = getSut(host = url.toString()) + + val exc = + assertThrows(PostHogApiError::class.java) { + sut.pushSubscription( + distinctId = "distinctId", + deviceToken = "fcm-token-123", + platform = "android", + appId = "firebase-project-id", + ) + } + assertEquals(400, exc.statusCode) + } + @Test fun `remoteConfig logs request headers in debug mode`() { val file = File("src/test/resources/json/basic-remote-config.json") diff --git a/posthog/src/test/java/com/posthog/internal/PostHogPushSubscriptionManagerTest.kt b/posthog/src/test/java/com/posthog/internal/PostHogPushSubscriptionManagerTest.kt new file mode 100644 index 00000000..5cfbfe38 --- /dev/null +++ b/posthog/src/test/java/com/posthog/internal/PostHogPushSubscriptionManagerTest.kt @@ -0,0 +1,234 @@ +package com.posthog.internal + +import com.posthog.API_KEY +import com.posthog.PostHogConfig +import com.posthog.mockHttp +import com.posthog.unGzip +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import java.io.File +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import kotlin.test.AfterTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +internal class PostHogPushSubscriptionManagerTest { + @get:Rule + val tmpDir = TemporaryFolder() + + private val executor: ExecutorService = Executors.newSingleThreadExecutor(PostHogThreadFactory("TestPushSub")) + + @AfterTest + fun `set down`() { + executor.shutdownNow() + tmpDir.root.deleteRecursively() + } + + private fun getSut( + http: MockWebServer, + storagePrefix: String = tmpDir.newFolder().absolutePath, + networkStatus: PostHogNetworkStatus? = null, + maxRetries: Int = 3, + ): Triple { + val config = + PostHogConfig(API_KEY, host = http.url("/").toString()).apply { + this.storagePrefix = storagePrefix + this.networkStatus = networkStatus + this.maxRetries = maxRetries + } + val api = PostHogApi(config) + return Triple(PostHogPushSubscriptionManager(config, api, executor), config, storagePrefix) + } + + private fun pendingFile( + storagePrefix: String, + ): File = File(File(storagePrefix, API_KEY), "push_subscription.pending") + + private fun flush() { + executor.submit {}.get() + } + + @Test + fun `register posts subscription and deletes pending file on success`() { + val http = mockHttp() + val (sut, _, storagePrefix) = getSut(http) + + sut.register("distinct-1", "fcm-token", "firebase-project", "android") + flush() + + val request = http.takeRequest() + assertEquals("POST", request.method) + assertEquals("/api/push_subscriptions/", request.path) + assertFalse(pendingFile(storagePrefix).exists()) + } + + @Test + fun `register defers when network is disconnected and keeps pending file`() { + val http = mockHttp() + val offline = + object : PostHogNetworkStatus { + override fun isConnected() = false + } + val (sut, _, storagePrefix) = getSut(http, networkStatus = offline) + + sut.register("distinct-1", "fcm-token", "firebase-project", "android") + flush() + + assertEquals(0, http.requestCount) + assertTrue(pendingFile(storagePrefix).exists()) + } + + @Test + fun `register deletes file on non-retryable failure`() { + val http = mockHttp(response = MockResponse().setResponseCode(400).setBody("bad")) + val (sut, _, storagePrefix) = getSut(http) + + sut.register("distinct-1", "fcm-token", "firebase-project", "android") + flush() + + assertEquals(1, http.requestCount) + assertFalse(pendingFile(storagePrefix).exists()) + } + + @Test + fun `register keeps file after retryable failure exhausts retries`() { + val http = mockHttp(response = MockResponse().setResponseCode(503).setBody("unavailable")) + // maxRetries = 0 means the first failure exhausts retries immediately, + // keeping the file for the next SDK start. + val (sut, _, storagePrefix) = getSut(http, maxRetries = 0) + + sut.register("distinct-1", "fcm-token", "firebase-project", "android") + flush() + + assertEquals(1, http.requestCount) + assertTrue(pendingFile(storagePrefix).exists()) + } + + @Test + fun `retryPending is a no-op when there is no pending file`() { + val http = mockHttp() + val (sut, _, _) = getSut(http) + + sut.retryPending() + flush() + + assertEquals(0, http.requestCount) + } + + @Test + fun `retryPending reads pending record and posts it`() { + val http = MockWebServer() + http.start() + // First response: 503 (retryable; with maxRetries=0 the file stays on disk). + http.enqueue(MockResponse().setResponseCode(503)) + // Second response: success (the retry after "restart"). + http.enqueue(MockResponse().setBody("")) + + val (sut, _, storagePrefix) = getSut(http, maxRetries = 0) + + sut.register("distinct-1", "fcm-token", "firebase-project", "android") + flush() + assertTrue(pendingFile(storagePrefix).exists()) + + sut.retryPending() + flush() + + assertEquals(2, http.requestCount) + assertFalse(pendingFile(storagePrefix).exists()) + http.shutdown() + } + + @Test + fun `register persists each call and deletes the file on success`() { + val http = mockHttp(total = 2, response = MockResponse().setBody("")) + val (sut, _, storagePrefix) = getSut(http) + + sut.register("distinct-1", "token-1", "firebase-project", "android") + flush() + sut.register("distinct-1", "token-2", "firebase-project", "android") + flush() + + assertEquals(2, http.requestCount) + val first = http.takeRequest().body.unGzip() + val second = http.takeRequest().body.unGzip() + assertTrue(first.contains("token-1")) + assertTrue(second.contains("token-2")) + assertFalse(pendingFile(storagePrefix).exists()) + } + + @Test + fun `register writes record that roundtrips through serializer`() { + val http = mockHttp(response = MockResponse().setResponseCode(503)) + val (sut, config, storagePrefix) = getSut(http, maxRetries = 0) + + sut.register("distinct-1", "fcm-token", "firebase-project", "android") + flush() + + val file = pendingFile(storagePrefix) + assertTrue(file.exists()) + + val record = + file.inputStream().use { + config.serializer.deserialize(it.reader().buffered()) + } + assertEquals("distinct-1", record?.distinctId) + assertEquals("fcm-token", record?.deviceToken) + assertEquals("firebase-project", record?.appId) + assertEquals("android", record?.platform) + } + + @Test + fun `retryPending removes corrupt pending file`() { + val http = mockHttp() + val (sut, _, storagePrefix) = getSut(http) + + // Write a file with junk bytes that can't be deserialized. + val file = pendingFile(storagePrefix) + file.parentFile.mkdirs() + file.writeText("{not valid json") + + sut.retryPending() + flush() + + assertEquals(0, http.requestCount) + assertFalse(file.exists()) + } + + @Test + fun `register with null storagePrefix still attempts the request`() { + val http = mockHttp() + val config = + PostHogConfig(API_KEY, host = http.url("/").toString()).apply { + this.storagePrefix = null + } + val api = PostHogApi(config) + val sut = PostHogPushSubscriptionManager(config, api, executor) + + sut.register("distinct-1", "fcm-token", "firebase-project", "android") + flush() + + assertEquals(1, http.requestCount) + } + + @Test + fun `retryPending is a no-op when storagePrefix is null`() { + val http = mockHttp() + val config = + PostHogConfig(API_KEY, host = http.url("/").toString()).apply { + this.storagePrefix = null + } + val api = PostHogApi(config) + val sut = PostHogPushSubscriptionManager(config, api, executor) + + sut.retryPending() + flush() + + assertEquals(0, http.requestCount) + } + +} From 200e4c2ceca5b95f9ad88712894e5220fbe461cd Mon Sep 17 00:00:00 2001 From: Haven Date: Thu, 23 Apr 2026 15:48:24 -0500 Subject: [PATCH 08/18] tweak --- build.gradle.kts | 1 + posthog-android/gradle.lockfile | 87 ++++++++++++++++++++++++++------- 2 files changed, 71 insertions(+), 17 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index c2fedb07..3932a622 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,6 +18,7 @@ plugins { id("org.jetbrains.kotlinx.binary-compatibility-validator") version PosthogBuildConfig.Plugins.API_VALIDATOR apply true id("com.github.gmazzo.buildconfig") version PosthogBuildConfig.Plugins.BUILD_CONFIG apply false id("ru.vyarus.animalsniffer") version PosthogBuildConfig.Plugins.ANIMAL_SNIFFER apply false + id("com.google.gms.google-services") version "4.4.2" apply false // TODO: add jacoco/codecov, gradle-versions-plugin } diff --git a/posthog-android/gradle.lockfile b/posthog-android/gradle.lockfile index 51fa7f24..2f6ed092 100644 --- a/posthog-android/gradle.lockfile +++ b/posthog-android/gradle.lockfile @@ -1,13 +1,19 @@ # This is a Gradle generated file for dependency locking. # Manual edits can break the build and are not advised. # This file is expected to be part of source control. -androidx.annotation:annotation-experimental:1.1.0=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata +androidx.activity:activity-ktx:1.7.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.activity:activity:1.7.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.annotation:annotation-experimental:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata androidx.annotation:annotation-jvm:1.7.0-beta01=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.annotation:annotation:1.2.0=compileOnlyDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath +androidx.annotation:annotation:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath +androidx.annotation:annotation:1.3.0=compileOnlyDependenciesMetadata androidx.annotation:annotation:1.7.0-beta01=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata +androidx.arch.core:core-common:2.0.0=compileOnlyDependenciesMetadata androidx.arch.core:core-common:2.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.arch.core:core-runtime:2.2.0=debugAndroidTestRuntimeClasspath,debugRuntimeClasspath,debugUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath -androidx.collection:collection:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.arch.core:core-runtime:2.0.0=compileOnlyDependenciesMetadata +androidx.arch.core:core-runtime:2.2.0=debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath +androidx.asynclayoutinflater:asynclayoutinflater:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +androidx.collection:collection:1.0.0=compileOnlyDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath androidx.compose.runtime:runtime-saveable:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath androidx.compose.runtime:runtime:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath androidx.compose.ui:ui-geometry:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath @@ -17,14 +23,43 @@ androidx.compose.ui:ui-unit:1.0.0=compileOnlyDependenciesMetadata,debugCompileCl androidx.compose.ui:ui:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath androidx.concurrent:concurrent-futures-ktx:1.1.0=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata androidx.concurrent:concurrent-futures:1.1.0=debugAndroidTestRuntimeClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata -androidx.core:core:1.5.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.coordinatorlayout:coordinatorlayout:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +androidx.core:core-ktx:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.core:core:1.2.0=compileOnlyDependenciesMetadata +androidx.core:core:1.8.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.cursoradapter:cursoradapter:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +androidx.customview:customview:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +androidx.documentfile:documentfile:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +androidx.drawerlayout:drawerlayout:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +androidx.fragment:fragment:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +androidx.interpolator:interpolator:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +androidx.legacy:legacy-support-core-ui:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +androidx.legacy:legacy-support-core-utils:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath androidx.lifecycle:lifecycle-common-java8:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.lifecycle:lifecycle-common:2.0.0=compileOnlyDependenciesMetadata androidx.lifecycle:lifecycle-common:2.3.1=testImplementationDependenciesMetadata androidx.lifecycle:lifecycle-common:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.lifecycle:lifecycle-livedata-core:2.0.0=compileOnlyDependenciesMetadata +androidx.lifecycle:lifecycle-livedata-core:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.lifecycle:lifecycle-livedata:2.0.0=compileOnlyDependenciesMetadata +androidx.lifecycle:lifecycle-livedata:2.6.2=debugCompileClasspath,releaseCompileClasspath androidx.lifecycle:lifecycle-process:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.lifecycle:lifecycle-runtime-ktx:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.lifecycle:lifecycle-runtime:2.0.0=compileOnlyDependenciesMetadata androidx.lifecycle:lifecycle-runtime:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.lifecycle:lifecycle-viewmodel:2.0.0=compileOnlyDependenciesMetadata +androidx.lifecycle:lifecycle-viewmodel:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.loader:loader:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +androidx.localbroadcastmanager:localbroadcastmanager:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +androidx.print:print:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath androidx.profileinstaller:profileinstaller:1.3.0=debugAndroidTestRuntimeClasspath,debugRuntimeClasspath,debugUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath +androidx.savedstate:savedstate-ktx:1.2.1=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.savedstate:savedstate:1.2.1=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.slidingpanelayout:slidingpanelayout:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath androidx.startup:startup-runtime:1.1.1=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.swiperefreshlayout:swiperefreshlayout:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath androidx.test.espresso:espresso-idling-resource:3.6.1=debugUnitTestRuntimeClasspath,releaseUnitTestRuntimeClasspath androidx.test.ext:junit:1.2.1=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata androidx.test.services:storage:1.5.0=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata @@ -36,7 +71,9 @@ androidx.test:rules:1.5.0=debugUnitTestCompileClasspath,debugUnitTestRuntimeClas androidx.test:runner:1.6.2=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata androidx.tracing:tracing:1.0.0=debugAndroidTestRuntimeClasspath,debugRuntimeClasspath,releaseRuntimeClasspath androidx.tracing:tracing:1.1.0=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata +androidx.versionedparcelable:versionedparcelable:1.1.0=compileOnlyDependenciesMetadata androidx.versionedparcelable:versionedparcelable:1.1.1=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.viewpager:viewpager:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath com.almworks.sqlite4java:sqlite4java:1.0.392=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata com.android.tools.ddms:ddmlib:31.9.1=_internal-unified-test-platform-android-device-provider-ddmlib com.android.tools.emulator:proto:31.9.1=_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-retention @@ -73,6 +110,11 @@ com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.12.7=dokkaGfmPart com.fasterxml.jackson.module:jackson-module-kotlin:2.12.7=dokkaGfmPartialPlugin,dokkaGfmPartialRuntime,dokkaGfmPlugin,dokkaGfmRuntime,dokkaHtmlPartialPlugin,dokkaHtmlPartialRuntime,dokkaHtmlPlugin,dokkaHtmlRuntime,dokkaJavadocPartialPlugin,dokkaJavadocPartialRuntime,dokkaJavadocPlugin,dokkaJavadocRuntime,dokkaJekyllPartialPlugin,dokkaJekyllPartialRuntime,dokkaJekyllPlugin,dokkaJekyllRuntime com.fasterxml.jackson:jackson-bom:2.12.7=dokkaGfmPartialPlugin,dokkaGfmPartialRuntime,dokkaGfmPlugin,dokkaGfmRuntime,dokkaHtmlPartialPlugin,dokkaHtmlPartialRuntime,dokkaHtmlPlugin,dokkaHtmlRuntime,dokkaJavadocPartialPlugin,dokkaJavadocPartialRuntime,dokkaJavadocPlugin,dokkaJavadocRuntime,dokkaJekyllPartialPlugin,dokkaJekyllPartialRuntime,dokkaJekyllPlugin,dokkaJekyllRuntime com.fasterxml.woodstox:woodstox-core:6.2.4=dokkaGfmPartialRuntime,dokkaGfmRuntime,dokkaHtmlPartialRuntime,dokkaHtmlRuntime,dokkaJavadocPartialRuntime,dokkaJavadocRuntime,dokkaJekyllPartialRuntime,dokkaJekyllRuntime +com.google.android.datatransport:transport-api:3.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +com.google.android.datatransport:transport-backend-cct:3.1.9=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +com.google.android.datatransport:transport-runtime:3.1.9=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +com.google.android.gms:play-services-basement:18.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +com.google.android.gms:play-services-tasks:18.0.1=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath com.google.android:annotations:4.1.1.4=_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core com.google.api.grpc:proto-google-common-protos:2.17.0=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core com.google.auto.service:auto-service-annotations:1.1.1=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle @@ -87,6 +129,19 @@ com.google.dagger:dagger:2.48=_internal-unified-test-platform-android-device-pro com.google.errorprone:error_prone_annotation:2.34.0=debugUnitTestRuntimeClasspath,releaseUnitTestRuntimeClasspath com.google.errorprone:error_prone_annotations:2.23.0=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core,_internal-unified-test-platform-launcher com.google.errorprone:error_prone_annotations:2.28.0=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata +com.google.firebase:firebase-annotations:16.2.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +com.google.firebase:firebase-common-ktx:21.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +com.google.firebase:firebase-common:21.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +com.google.firebase:firebase-components:18.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +com.google.firebase:firebase-datatransport:18.2.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +com.google.firebase:firebase-encoders-json:18.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +com.google.firebase:firebase-encoders-proto:16.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +com.google.firebase:firebase-encoders:17.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +com.google.firebase:firebase-iid-interop:17.1.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +com.google.firebase:firebase-installations-interop:17.1.1=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +com.google.firebase:firebase-installations:17.2.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +com.google.firebase:firebase-measurement-connector:19.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +com.google.firebase:firebase-messaging:24.1.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath com.google.guava:failureaccess:1.0.1=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core,_internal-unified-test-platform-launcher com.google.guava:failureaccess:1.0.2=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata com.google.guava:guava:32.0.1-jre=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core,_internal-unified-test-platform-launcher @@ -140,7 +195,7 @@ it.unimi.dsi:fastutil-core:8.5.12=dokkaGfmPartialPlugin,dokkaGfmPlugin,dokkaHtml jakarta.activation:jakarta.activation-api:1.2.1=dokkaGfmPartialRuntime,dokkaGfmRuntime,dokkaHtmlPartialRuntime,dokkaHtmlRuntime,dokkaJavadocPartialRuntime,dokkaJavadocRuntime,dokkaJekyllPartialRuntime,dokkaJekyllRuntime jakarta.xml.bind:jakarta.xml.bind-api:2.3.2=dokkaGfmPartialRuntime,dokkaGfmRuntime,dokkaHtmlPartialRuntime,dokkaHtmlRuntime,dokkaJavadocPartialRuntime,dokkaJavadocRuntime,dokkaJekyllPartialRuntime,dokkaJekyllRuntime javax.annotation:javax.annotation-api:1.3.2=_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata -javax.inject:javax.inject:1=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata +javax.inject:javax.inject:1=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core,compileOnlyDependenciesMetadata,debugCompileClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata junit:junit:4.13.2=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata net.bytebuddy:byte-buddy-agent:1.12.19=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata net.bytebuddy:byte-buddy:1.12.19=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata @@ -182,24 +237,24 @@ org.jetbrains.kotlin:kotlin-scripting-common:2.1.10=kotlinBuildToolsApiClasspath org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:2.1.10=kotlinBuildToolsApiClasspath org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:2.1.10=kotlinBuildToolsApiClasspath org.jetbrains.kotlin:kotlin-scripting-jvm:2.1.10=kotlinBuildToolsApiClasspath -org.jetbrains.kotlin:kotlin-stdlib-common:1.5.0=compileOnlyDependenciesMetadata org.jetbrains.kotlin:kotlin-stdlib-common:1.8.21=_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-core +org.jetbrains.kotlin:kotlin-stdlib-common:1.8.22=compileOnlyDependenciesMetadata org.jetbrains.kotlin:kotlin-stdlib-common:1.9.0=_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-launcher org.jetbrains.kotlin:kotlin-stdlib-common:1.9.22=dokkaGfmPartialPlugin,dokkaGfmPartialRuntime,dokkaGfmPlugin,dokkaGfmRuntime,dokkaHtmlPartialPlugin,dokkaHtmlPartialRuntime,dokkaHtmlPlugin,dokkaHtmlRuntime,dokkaJavadocPartialPlugin,dokkaJavadocPartialRuntime,dokkaJavadocPlugin,dokkaJavadocRuntime,dokkaJekyllPartialPlugin,dokkaJekyllPartialRuntime,dokkaJekyllPlugin,dokkaJekyllRuntime org.jetbrains.kotlin:kotlin-stdlib-common:2.1.0=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle org.jetbrains.kotlin:kotlin-stdlib-common:2.1.10=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.0=compileOnlyDependenciesMetadata org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.20=_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-core,_internal-unified-test-platform-launcher,dokkaGfmPartialRuntime,dokkaGfmRuntime,dokkaHtmlPartialRuntime,dokkaHtmlRuntime,dokkaJavadocPartialRuntime,dokkaJavadocRuntime,dokkaJekyllPartialRuntime,dokkaJekyllRuntime,testImplementationDependenciesMetadata +org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.22=compileOnlyDependenciesMetadata org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0=dokkaGfmPartialPlugin,dokkaGfmPlugin,dokkaHtmlPartialPlugin,dokkaHtmlPlugin,dokkaJavadocPartialPlugin,dokkaJavadocPlugin,dokkaJekyllPartialPlugin,dokkaJekyllPlugin org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.1.0=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.1.10=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.0=compileOnlyDependenciesMetadata org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.20=_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-core,_internal-unified-test-platform-launcher,dokkaGfmPartialRuntime,dokkaGfmRuntime,dokkaHtmlPartialRuntime,dokkaHtmlRuntime,dokkaJavadocPartialRuntime,dokkaJavadocRuntime,dokkaJekyllPartialRuntime,dokkaJekyllRuntime,testImplementationDependenciesMetadata +org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22=compileOnlyDependenciesMetadata org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0=dokkaGfmPartialPlugin,dokkaGfmPlugin,dokkaHtmlPartialPlugin,dokkaHtmlPlugin,dokkaJavadocPartialPlugin,dokkaJavadocPlugin,dokkaJekyllPartialPlugin,dokkaJekyllPlugin org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.0=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.10=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -org.jetbrains.kotlin:kotlin-stdlib:1.5.0=compileOnlyDependenciesMetadata org.jetbrains.kotlin:kotlin-stdlib:1.8.21=_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-core +org.jetbrains.kotlin:kotlin-stdlib:1.8.22=compileOnlyDependenciesMetadata org.jetbrains.kotlin:kotlin-stdlib:1.9.0=_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-launcher org.jetbrains.kotlin:kotlin-stdlib:1.9.22=dokkaGfmPartialPlugin,dokkaGfmPartialRuntime,dokkaGfmPlugin,dokkaGfmRuntime,dokkaHtmlPartialPlugin,dokkaHtmlPartialRuntime,dokkaHtmlPlugin,dokkaHtmlRuntime,dokkaJavadocPartialPlugin,dokkaJavadocPartialRuntime,dokkaJavadocPlugin,dokkaJavadocRuntime,dokkaJekyllPartialPlugin,dokkaJekyllPartialRuntime,dokkaJekyllPlugin,dokkaJekyllRuntime org.jetbrains.kotlin:kotlin-stdlib:2.1.0=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle @@ -207,23 +262,21 @@ org.jetbrains.kotlin:kotlin-stdlib:2.1.10=bcv-rt-jvm-cp-resolver,debugAndroidTes org.jetbrains.kotlin:kotlin-test-junit:2.1.10=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata org.jetbrains.kotlin:kotlin-test:2.1.10=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata org.jetbrains.kotlinx:atomicfu-jvm:0.22.0=_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-launcher -org.jetbrains.kotlinx:atomicfu:0.16.1=compileOnlyDependenciesMetadata -org.jetbrains.kotlinx:atomicfu:0.17.3=implementationDependenciesMetadata +org.jetbrains.kotlinx:atomicfu:0.17.3=compileOnlyDependenciesMetadata,implementationDependenciesMetadata org.jetbrains.kotlinx:atomicfu:0.20.2=testImplementationDependenciesMetadata org.jetbrains.kotlinx:atomicfu:0.22.0=_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-launcher -org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0=compileOnlyDependenciesMetadata -org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath +org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4=compileOnlyDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath +org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4=compileOnlyDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.1=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core,_internal-unified-test-platform-launcher,dokkaGfmPartialPlugin,dokkaGfmPartialRuntime,dokkaGfmPlugin,dokkaGfmRuntime,dokkaHtmlPartialPlugin,dokkaHtmlPartialRuntime,dokkaHtmlPlugin,dokkaHtmlRuntime,dokkaJavadocPartialPlugin,dokkaJavadocPartialRuntime,dokkaJavadocPlugin,dokkaJavadocRuntime,dokkaJekyllPartialPlugin,dokkaJekyllPartialRuntime,dokkaJekyllPlugin,dokkaJekyllRuntime org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath,releaseCompileClasspath,releaseRuntimeClasspath org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.1=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core,_internal-unified-test-platform-launcher,dokkaGfmPartialPlugin,dokkaGfmPartialRuntime,dokkaGfmPlugin,dokkaGfmRuntime,dokkaHtmlPartialPlugin,dokkaHtmlPartialRuntime,dokkaHtmlPlugin,dokkaHtmlRuntime,dokkaJavadocPartialPlugin,dokkaJavadocPartialRuntime,dokkaJavadocPlugin,dokkaJavadocRuntime,dokkaJekyllPartialPlugin,dokkaJekyllPartialRuntime,dokkaJekyllPlugin,dokkaJekyllRuntime -org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0=compileOnlyDependenciesMetadata -org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath +org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4=compileOnlyDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core,_internal-unified-test-platform-launcher,dokkaGfmPartialPlugin,dokkaGfmPartialRuntime,dokkaGfmPlugin,dokkaGfmRuntime,dokkaHtmlPartialPlugin,dokkaHtmlPartialRuntime,dokkaHtmlPlugin,dokkaHtmlRuntime,dokkaJavadocPartialPlugin,dokkaJavadocPartialRuntime,dokkaJavadocPlugin,dokkaJavadocRuntime,dokkaJekyllPartialPlugin,dokkaJekyllPartialRuntime,dokkaJekyllPlugin,dokkaJekyllRuntime +org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.4=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath org.jetbrains.kotlinx:kotlinx-html-jvm:0.9.1=dokkaGfmPartialPlugin,dokkaGfmPlugin,dokkaHtmlPartialPlugin,dokkaHtmlPlugin,dokkaJavadocPartialPlugin,dokkaJavadocPlugin,dokkaJekyllPartialPlugin,dokkaJekyllPlugin org.jetbrains.kotlinx:kover-jvm-agent:0.9.0=koverJvmAgent,koverJvmReporter org.jetbrains:annotations:13.0=bcv-rt-jvm-cp-resolver,compileOnlyDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath,releaseCompileClasspath,releaseRuntimeClasspath From af16367a97e672f51c2f0a0e9843dd5529fdd200 Mon Sep 17 00:00:00 2001 From: Haven Date: Thu, 23 Apr 2026 15:50:04 -0500 Subject: [PATCH 09/18] update changelog --- .changeset/strong-moments-strive.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/strong-moments-strive.md diff --git a/.changeset/strong-moments-strive.md b/.changeset/strong-moments-strive.md new file mode 100644 index 00000000..cbd63c40 --- /dev/null +++ b/.changeset/strong-moments-strive.md @@ -0,0 +1,5 @@ +--- +"posthog-android": minor +--- + +Add support for initiating push notification subscriptions using PostHog Workflows From bbcd79faf7113a3f62fe26708e26b4315b53e03a Mon Sep 17 00:00:00 2001 From: Haven Date: Thu, 23 Apr 2026 15:56:50 -0500 Subject: [PATCH 10/18] fix lint errors --- .../posthog/internal/PostHogPushSubscriptionManager.kt | 9 +++++---- .../internal/PostHogPushSubscriptionManagerTest.kt | 5 +---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionManager.kt b/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionManager.kt index e42aa304..a0e49e37 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionManager.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionManager.kt @@ -61,10 +61,11 @@ internal class PostHogPushSubscriptionManager( if (!file.existsSafely(config)) { return@executeSafely } - val record = readRecord(file) ?: run { - file.deleteSafely(config) - return@executeSafely - } + val record = + readRecord(file) ?: run { + file.deleteSafely(config) + return@executeSafely + } retryCount = 0 cancelTimer() attempt(record, file) diff --git a/posthog/src/test/java/com/posthog/internal/PostHogPushSubscriptionManagerTest.kt b/posthog/src/test/java/com/posthog/internal/PostHogPushSubscriptionManagerTest.kt index 5cfbfe38..d906d26b 100644 --- a/posthog/src/test/java/com/posthog/internal/PostHogPushSubscriptionManagerTest.kt +++ b/posthog/src/test/java/com/posthog/internal/PostHogPushSubscriptionManagerTest.kt @@ -45,9 +45,7 @@ internal class PostHogPushSubscriptionManagerTest { return Triple(PostHogPushSubscriptionManager(config, api, executor), config, storagePrefix) } - private fun pendingFile( - storagePrefix: String, - ): File = File(File(storagePrefix, API_KEY), "push_subscription.pending") + private fun pendingFile(storagePrefix: String): File = File(File(storagePrefix, API_KEY), "push_subscription.pending") private fun flush() { executor.submit {}.get() @@ -230,5 +228,4 @@ internal class PostHogPushSubscriptionManagerTest { assertEquals(0, http.requestCount) } - } From 249c7c21044069a43247988e4ef8266c62b7bf5d Mon Sep 17 00:00:00 2001 From: Haven Date: Fri, 24 Apr 2026 16:40:48 -0500 Subject: [PATCH 11/18] regenerate api file --- posthog/api/posthog.api | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/posthog/api/posthog.api b/posthog/api/posthog.api index d633c274..d2a61363 100644 --- a/posthog/api/posthog.api +++ b/posthog/api/posthog.api @@ -48,6 +48,7 @@ public final class com/posthog/PostHog : com/posthog/PostHogStateless, com/posth public fun optIn ()V public fun optOut ()V public fun register (Ljava/lang/String;Ljava/lang/Object;)V + public fun registerPushNotificationToken (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public fun reloadFeatureFlags (Lcom/posthog/PostHogOnFeatureFlags;)V public fun reset ()V public fun resetGroupPropertiesForFlags (Ljava/lang/String;Z)V @@ -91,6 +92,7 @@ public final class com/posthog/PostHog$Companion : com/posthog/PostHogInterface public fun optOut ()V public final fun overrideSharedInstance (Lcom/posthog/PostHogInterface;)V public fun register (Ljava/lang/String;Ljava/lang/Object;)V + public fun registerPushNotificationToken (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public fun reloadFeatureFlags (Lcom/posthog/PostHogOnFeatureFlags;)V public fun reset ()V public fun resetGroupPropertiesForFlags (Ljava/lang/String;Z)V @@ -326,6 +328,7 @@ public abstract interface class com/posthog/PostHogInterface : com/posthog/PostH public abstract fun isSessionActive ()Z public abstract fun isSessionReplayActive ()Z public abstract fun register (Ljava/lang/String;Ljava/lang/Object;)V + public abstract fun registerPushNotificationToken (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public abstract fun reloadFeatureFlags (Lcom/posthog/PostHogOnFeatureFlags;)V public abstract fun reset ()V public abstract fun resetGroupPropertiesForFlags (Ljava/lang/String;Z)V @@ -350,6 +353,7 @@ public final class com/posthog/PostHogInterface$DefaultImpls { public static synthetic fun getFeatureFlagResult$default (Lcom/posthog/PostHogInterface;Ljava/lang/String;Ljava/lang/Boolean;ILjava/lang/Object;)Lcom/posthog/FeatureFlagResult; public static synthetic fun group$default (Lcom/posthog/PostHogInterface;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)V public static synthetic fun isFeatureEnabled$default (Lcom/posthog/PostHogInterface;Ljava/lang/String;ZLjava/lang/Boolean;ILjava/lang/Object;)Z + public static synthetic fun registerPushNotificationToken$default (Lcom/posthog/PostHogInterface;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V public static synthetic fun reloadFeatureFlags$default (Lcom/posthog/PostHogInterface;Lcom/posthog/PostHogOnFeatureFlags;ILjava/lang/Object;)V public static synthetic fun resetGroupPropertiesForFlags$default (Lcom/posthog/PostHogInterface;Ljava/lang/String;ZILjava/lang/Object;)V public static synthetic fun resetPersonPropertiesForFlags$default (Lcom/posthog/PostHogInterface;ZILjava/lang/Object;)V @@ -644,6 +648,7 @@ public final class com/posthog/internal/PostHogApi { public static synthetic fun flags$default (Lcom/posthog/internal/PostHogApi;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;ILjava/lang/Object;)Lcom/posthog/internal/PostHogFlagsResponse; public final fun localEvaluation (Ljava/lang/String;Ljava/lang/String;)Lcom/posthog/internal/LocalEvaluationApiResponse; public static synthetic fun localEvaluation$default (Lcom/posthog/internal/PostHogApi;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/posthog/internal/LocalEvaluationApiResponse; + public final fun pushSubscription (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public final fun remoteConfig ()Lcom/posthog/internal/PostHogRemoteConfigResponse; public final fun snapshot (Ljava/util/List;)V } From 4ae09a372ba099abb2d465f9711101d1adf9ae84 Mon Sep 17 00:00:00 2001 From: Haven Date: Tue, 28 Apr 2026 12:24:56 -0500 Subject: [PATCH 12/18] update api file --- posthog-android/api/posthog-android.api | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/posthog-android/api/posthog-android.api b/posthog-android/api/posthog-android.api index d8fff6e5..7837aaf4 100644 --- a/posthog-android/api/posthog-android.api +++ b/posthog-android/api/posthog-android.api @@ -33,6 +33,19 @@ public class com/posthog/android/PostHogAndroidConfig : com/posthog/PostHogConfi public final fun setSessionReplayConfig (Lcom/posthog/android/replay/PostHogSessionReplayConfig;)V } +public final class com/posthog/android/PostHogPushNotifications { + public static final field INSTANCE Lcom/posthog/android/PostHogPushNotifications; + public static final fun hasPermission (Landroid/app/Activity;)Z + public static final fun registerPermissionLauncher (Landroidx/activity/ComponentActivity;)Lcom/posthog/android/PostHogPushPermissionLauncher; + public static final fun registerPermissionLauncher (Landroidx/activity/ComponentActivity;Lkotlin/jvm/functions/Function1;)Lcom/posthog/android/PostHogPushPermissionLauncher; + public static synthetic fun registerPermissionLauncher$default (Landroidx/activity/ComponentActivity;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/posthog/android/PostHogPushPermissionLauncher; + public static final fun registerToken (Ljava/lang/String;Ljava/lang/String;)V +} + +public final class com/posthog/android/PostHogPushPermissionLauncher { + public final fun launch ()V +} + public final class com/posthog/android/internal/MainHandler { public fun ()V public fun (Landroid/os/Looper;)V From 55df64af31a2a9ae7870b24e79c849125a10ccd2 Mon Sep 17 00:00:00 2001 From: Haven Date: Tue, 28 Apr 2026 17:56:50 -0500 Subject: [PATCH 13/18] fix release check --- .../main/java/com/posthog/android/PostHogPushNotifications.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt b/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt index 4a0cac65..fd942392 100644 --- a/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt +++ b/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt @@ -30,7 +30,9 @@ public class PostHogPushPermissionLauncher internal constructor( PostHogPushNotifications.fetchAndRegisterFcmToken(activity) return } - permissionLauncher?.launch(Manifest.permission.POST_NOTIFICATIONS) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + permissionLauncher?.launch(Manifest.permission.POST_NOTIFICATIONS) + } } } From 490de530b263a8b39ef8907be4db47939733dbc3 Mon Sep 17 00:00:00 2001 From: Haven Date: Tue, 28 Apr 2026 18:23:59 -0500 Subject: [PATCH 14/18] tweak --- .../main/java/com/posthog/android/PostHogPushNotifications.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt b/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt index fd942392..f24fa9c5 100644 --- a/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt +++ b/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt @@ -71,6 +71,7 @@ public object PostHogPushNotifications { */ @JvmStatic @JvmOverloads + @Suppress("InvalidFragmentVersionForActivityResult") public fun registerPermissionLauncher( activity: ComponentActivity, onPermissionResult: ((Boolean) -> Unit)? = null, From a0a65d42474c2d21b100e357e80a111d109e60af Mon Sep 17 00:00:00 2001 From: Haven Date: Wed, 29 Apr 2026 10:22:46 -0500 Subject: [PATCH 15/18] tweak --- .../java/com/posthog/android/internal/PostHogAndroidUtils.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/posthog-android/src/main/java/com/posthog/android/internal/PostHogAndroidUtils.kt b/posthog-android/src/main/java/com/posthog/android/internal/PostHogAndroidUtils.kt index ba65b3f9..e5deb565 100644 --- a/posthog-android/src/main/java/com/posthog/android/internal/PostHogAndroidUtils.kt +++ b/posthog-android/src/main/java/com/posthog/android/internal/PostHogAndroidUtils.kt @@ -14,6 +14,7 @@ import android.graphics.Point import android.net.ConnectivityManager import android.net.Uri import android.os.Build +import androidx.core.net.toUri import android.os.Process import android.telephony.TelephonyManager import android.util.Base64 @@ -178,7 +179,7 @@ internal fun Intent.getReferrerInfo(config: PostHogAndroidConfig): Map Date: Wed, 29 Apr 2026 12:47:36 -0500 Subject: [PATCH 16/18] address comments --- .changeset/strong-moments-strive.md | 1 + build.gradle.kts | 1 - buildSrc/src/main/java/PosthogBuildConfig.kt | 2 - posthog-android/api/posthog-android.api | 13 -- posthog-android/build.gradle.kts | 2 - posthog-android/gradle.lockfile | 87 ++------ .../android/PostHogPushNotifications.kt | 187 ------------------ .../android/PostHogPushNotificationsTest.kt | 38 ---- posthog/src/main/java/com/posthog/PostHog.kt | 1 + .../PostHogPushSubscriptionManager.kt | 5 + 10 files changed, 24 insertions(+), 313 deletions(-) delete mode 100644 posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt delete mode 100644 posthog-android/src/test/java/com/posthog/android/PostHogPushNotificationsTest.kt diff --git a/.changeset/strong-moments-strive.md b/.changeset/strong-moments-strive.md index cbd63c40..f8303028 100644 --- a/.changeset/strong-moments-strive.md +++ b/.changeset/strong-moments-strive.md @@ -1,5 +1,6 @@ --- "posthog-android": minor +"posthog": minor --- Add support for initiating push notification subscriptions using PostHog Workflows diff --git a/build.gradle.kts b/build.gradle.kts index 3932a622..c2fedb07 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,7 +18,6 @@ plugins { id("org.jetbrains.kotlinx.binary-compatibility-validator") version PosthogBuildConfig.Plugins.API_VALIDATOR apply true id("com.github.gmazzo.buildconfig") version PosthogBuildConfig.Plugins.BUILD_CONFIG apply false id("ru.vyarus.animalsniffer") version PosthogBuildConfig.Plugins.ANIMAL_SNIFFER apply false - id("com.google.gms.google-services") version "4.4.2" apply false // TODO: add jacoco/codecov, gradle-versions-plugin } diff --git a/buildSrc/src/main/java/PosthogBuildConfig.kt b/buildSrc/src/main/java/PosthogBuildConfig.kt index fe2e1d8a..ce07c689 100644 --- a/buildSrc/src/main/java/PosthogBuildConfig.kt +++ b/buildSrc/src/main/java/PosthogBuildConfig.kt @@ -68,8 +68,6 @@ object PosthogBuildConfig { val CURTAINS = "1.2.5" val ANDROIDX_CORE = "1.5.0" val ANDROIDX_COMPOSE = "1.0.0" - val ANDROIDX_ACTIVITY = "1.7.2" - val FIREBASE_MESSAGING = "24.1.0" // tests val ANDROIDX_JUNIT = "1.2.1" diff --git a/posthog-android/api/posthog-android.api b/posthog-android/api/posthog-android.api index 7837aaf4..d8fff6e5 100644 --- a/posthog-android/api/posthog-android.api +++ b/posthog-android/api/posthog-android.api @@ -33,19 +33,6 @@ public class com/posthog/android/PostHogAndroidConfig : com/posthog/PostHogConfi public final fun setSessionReplayConfig (Lcom/posthog/android/replay/PostHogSessionReplayConfig;)V } -public final class com/posthog/android/PostHogPushNotifications { - public static final field INSTANCE Lcom/posthog/android/PostHogPushNotifications; - public static final fun hasPermission (Landroid/app/Activity;)Z - public static final fun registerPermissionLauncher (Landroidx/activity/ComponentActivity;)Lcom/posthog/android/PostHogPushPermissionLauncher; - public static final fun registerPermissionLauncher (Landroidx/activity/ComponentActivity;Lkotlin/jvm/functions/Function1;)Lcom/posthog/android/PostHogPushPermissionLauncher; - public static synthetic fun registerPermissionLauncher$default (Landroidx/activity/ComponentActivity;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/posthog/android/PostHogPushPermissionLauncher; - public static final fun registerToken (Ljava/lang/String;Ljava/lang/String;)V -} - -public final class com/posthog/android/PostHogPushPermissionLauncher { - public final fun launch ()V -} - public final class com/posthog/android/internal/MainHandler { public fun ()V public fun (Landroid/os/Looper;)V diff --git a/posthog-android/build.gradle.kts b/posthog-android/build.gradle.kts index e737efa9..d97a3787 100644 --- a/posthog-android/build.gradle.kts +++ b/posthog-android/build.gradle.kts @@ -93,12 +93,10 @@ dependencies { implementation("androidx.lifecycle:lifecycle-process:${PosthogBuildConfig.Dependencies.LIFECYCLE}") implementation("androidx.lifecycle:lifecycle-common-java8:${PosthogBuildConfig.Dependencies.LIFECYCLE}") implementation("androidx.core:core:${PosthogBuildConfig.Dependencies.ANDROIDX_CORE}") - implementation("androidx.activity:activity-ktx:${PosthogBuildConfig.Dependencies.ANDROIDX_ACTIVITY}") implementation("com.squareup.curtains:curtains:${PosthogBuildConfig.Dependencies.CURTAINS}") // compile only compileOnly("androidx.compose.ui:ui:${PosthogBuildConfig.Dependencies.ANDROIDX_COMPOSE}") - compileOnly("com.google.firebase:firebase-messaging:${PosthogBuildConfig.Dependencies.FIREBASE_MESSAGING}") // compatibility signature("org.codehaus.mojo.signature:java18:${PosthogBuildConfig.Plugins.SIGNATURE_JAVA18}@signature") diff --git a/posthog-android/gradle.lockfile b/posthog-android/gradle.lockfile index 2f6ed092..51fa7f24 100644 --- a/posthog-android/gradle.lockfile +++ b/posthog-android/gradle.lockfile @@ -1,19 +1,13 @@ # This is a Gradle generated file for dependency locking. # Manual edits can break the build and are not advised. # This file is expected to be part of source control. -androidx.activity:activity-ktx:1.7.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.activity:activity:1.7.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.annotation:annotation-experimental:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata +androidx.annotation:annotation-experimental:1.1.0=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata androidx.annotation:annotation-jvm:1.7.0-beta01=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.annotation:annotation:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath -androidx.annotation:annotation:1.3.0=compileOnlyDependenciesMetadata +androidx.annotation:annotation:1.2.0=compileOnlyDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath androidx.annotation:annotation:1.7.0-beta01=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata -androidx.arch.core:core-common:2.0.0=compileOnlyDependenciesMetadata androidx.arch.core:core-common:2.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.arch.core:core-runtime:2.0.0=compileOnlyDependenciesMetadata -androidx.arch.core:core-runtime:2.2.0=debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath -androidx.asynclayoutinflater:asynclayoutinflater:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -androidx.collection:collection:1.0.0=compileOnlyDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +androidx.arch.core:core-runtime:2.2.0=debugAndroidTestRuntimeClasspath,debugRuntimeClasspath,debugUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath +androidx.collection:collection:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath androidx.compose.runtime:runtime-saveable:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath androidx.compose.runtime:runtime:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath androidx.compose.ui:ui-geometry:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath @@ -23,43 +17,14 @@ androidx.compose.ui:ui-unit:1.0.0=compileOnlyDependenciesMetadata,debugCompileCl androidx.compose.ui:ui:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath androidx.concurrent:concurrent-futures-ktx:1.1.0=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata androidx.concurrent:concurrent-futures:1.1.0=debugAndroidTestRuntimeClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata -androidx.coordinatorlayout:coordinatorlayout:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -androidx.core:core-ktx:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.core:core:1.2.0=compileOnlyDependenciesMetadata -androidx.core:core:1.8.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.cursoradapter:cursoradapter:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -androidx.customview:customview:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -androidx.documentfile:documentfile:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -androidx.drawerlayout:drawerlayout:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -androidx.fragment:fragment:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -androidx.interpolator:interpolator:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -androidx.legacy:legacy-support-core-ui:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -androidx.legacy:legacy-support-core-utils:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath +androidx.core:core:1.5.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath androidx.lifecycle:lifecycle-common-java8:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.lifecycle:lifecycle-common:2.0.0=compileOnlyDependenciesMetadata androidx.lifecycle:lifecycle-common:2.3.1=testImplementationDependenciesMetadata androidx.lifecycle:lifecycle-common:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.lifecycle:lifecycle-livedata-core:2.0.0=compileOnlyDependenciesMetadata -androidx.lifecycle:lifecycle-livedata-core:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.lifecycle:lifecycle-livedata:2.0.0=compileOnlyDependenciesMetadata -androidx.lifecycle:lifecycle-livedata:2.6.2=debugCompileClasspath,releaseCompileClasspath androidx.lifecycle:lifecycle-process:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.lifecycle:lifecycle-runtime-ktx:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.lifecycle:lifecycle-runtime:2.0.0=compileOnlyDependenciesMetadata androidx.lifecycle:lifecycle-runtime:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.lifecycle:lifecycle-viewmodel:2.0.0=compileOnlyDependenciesMetadata -androidx.lifecycle:lifecycle-viewmodel:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.loader:loader:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -androidx.localbroadcastmanager:localbroadcastmanager:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -androidx.print:print:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath androidx.profileinstaller:profileinstaller:1.3.0=debugAndroidTestRuntimeClasspath,debugRuntimeClasspath,debugUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestRuntimeClasspath -androidx.savedstate:savedstate-ktx:1.2.1=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.savedstate:savedstate:1.2.1=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.slidingpanelayout:slidingpanelayout:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath androidx.startup:startup-runtime:1.1.1=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.swiperefreshlayout:swiperefreshlayout:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath androidx.test.espresso:espresso-idling-resource:3.6.1=debugUnitTestRuntimeClasspath,releaseUnitTestRuntimeClasspath androidx.test.ext:junit:1.2.1=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata androidx.test.services:storage:1.5.0=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata @@ -71,9 +36,7 @@ androidx.test:rules:1.5.0=debugUnitTestCompileClasspath,debugUnitTestRuntimeClas androidx.test:runner:1.6.2=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata androidx.tracing:tracing:1.0.0=debugAndroidTestRuntimeClasspath,debugRuntimeClasspath,releaseRuntimeClasspath androidx.tracing:tracing:1.1.0=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata -androidx.versionedparcelable:versionedparcelable:1.1.0=compileOnlyDependenciesMetadata androidx.versionedparcelable:versionedparcelable:1.1.1=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -androidx.viewpager:viewpager:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath com.almworks.sqlite4java:sqlite4java:1.0.392=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata com.android.tools.ddms:ddmlib:31.9.1=_internal-unified-test-platform-android-device-provider-ddmlib com.android.tools.emulator:proto:31.9.1=_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-retention @@ -110,11 +73,6 @@ com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.12.7=dokkaGfmPart com.fasterxml.jackson.module:jackson-module-kotlin:2.12.7=dokkaGfmPartialPlugin,dokkaGfmPartialRuntime,dokkaGfmPlugin,dokkaGfmRuntime,dokkaHtmlPartialPlugin,dokkaHtmlPartialRuntime,dokkaHtmlPlugin,dokkaHtmlRuntime,dokkaJavadocPartialPlugin,dokkaJavadocPartialRuntime,dokkaJavadocPlugin,dokkaJavadocRuntime,dokkaJekyllPartialPlugin,dokkaJekyllPartialRuntime,dokkaJekyllPlugin,dokkaJekyllRuntime com.fasterxml.jackson:jackson-bom:2.12.7=dokkaGfmPartialPlugin,dokkaGfmPartialRuntime,dokkaGfmPlugin,dokkaGfmRuntime,dokkaHtmlPartialPlugin,dokkaHtmlPartialRuntime,dokkaHtmlPlugin,dokkaHtmlRuntime,dokkaJavadocPartialPlugin,dokkaJavadocPartialRuntime,dokkaJavadocPlugin,dokkaJavadocRuntime,dokkaJekyllPartialPlugin,dokkaJekyllPartialRuntime,dokkaJekyllPlugin,dokkaJekyllRuntime com.fasterxml.woodstox:woodstox-core:6.2.4=dokkaGfmPartialRuntime,dokkaGfmRuntime,dokkaHtmlPartialRuntime,dokkaHtmlRuntime,dokkaJavadocPartialRuntime,dokkaJavadocRuntime,dokkaJekyllPartialRuntime,dokkaJekyllRuntime -com.google.android.datatransport:transport-api:3.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -com.google.android.datatransport:transport-backend-cct:3.1.9=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -com.google.android.datatransport:transport-runtime:3.1.9=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -com.google.android.gms:play-services-basement:18.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -com.google.android.gms:play-services-tasks:18.0.1=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath com.google.android:annotations:4.1.1.4=_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core com.google.api.grpc:proto-google-common-protos:2.17.0=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core com.google.auto.service:auto-service-annotations:1.1.1=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle @@ -129,19 +87,6 @@ com.google.dagger:dagger:2.48=_internal-unified-test-platform-android-device-pro com.google.errorprone:error_prone_annotation:2.34.0=debugUnitTestRuntimeClasspath,releaseUnitTestRuntimeClasspath com.google.errorprone:error_prone_annotations:2.23.0=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core,_internal-unified-test-platform-launcher com.google.errorprone:error_prone_annotations:2.28.0=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata -com.google.firebase:firebase-annotations:16.2.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -com.google.firebase:firebase-common-ktx:21.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -com.google.firebase:firebase-common:21.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -com.google.firebase:firebase-components:18.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -com.google.firebase:firebase-datatransport:18.2.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -com.google.firebase:firebase-encoders-json:18.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -com.google.firebase:firebase-encoders-proto:16.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -com.google.firebase:firebase-encoders:17.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -com.google.firebase:firebase-iid-interop:17.1.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -com.google.firebase:firebase-installations-interop:17.1.1=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -com.google.firebase:firebase-installations:17.2.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -com.google.firebase:firebase-measurement-connector:19.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath -com.google.firebase:firebase-messaging:24.1.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath com.google.guava:failureaccess:1.0.1=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core,_internal-unified-test-platform-launcher com.google.guava:failureaccess:1.0.2=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata com.google.guava:guava:32.0.1-jre=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core,_internal-unified-test-platform-launcher @@ -195,7 +140,7 @@ it.unimi.dsi:fastutil-core:8.5.12=dokkaGfmPartialPlugin,dokkaGfmPlugin,dokkaHtml jakarta.activation:jakarta.activation-api:1.2.1=dokkaGfmPartialRuntime,dokkaGfmRuntime,dokkaHtmlPartialRuntime,dokkaHtmlRuntime,dokkaJavadocPartialRuntime,dokkaJavadocRuntime,dokkaJekyllPartialRuntime,dokkaJekyllRuntime jakarta.xml.bind:jakarta.xml.bind-api:2.3.2=dokkaGfmPartialRuntime,dokkaGfmRuntime,dokkaHtmlPartialRuntime,dokkaHtmlRuntime,dokkaJavadocPartialRuntime,dokkaJavadocRuntime,dokkaJekyllPartialRuntime,dokkaJekyllRuntime javax.annotation:javax.annotation-api:1.3.2=_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata -javax.inject:javax.inject:1=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core,compileOnlyDependenciesMetadata,debugCompileClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata +javax.inject:javax.inject:1=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata junit:junit:4.13.2=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata net.bytebuddy:byte-buddy-agent:1.12.19=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata net.bytebuddy:byte-buddy:1.12.19=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata @@ -237,24 +182,24 @@ org.jetbrains.kotlin:kotlin-scripting-common:2.1.10=kotlinBuildToolsApiClasspath org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:2.1.10=kotlinBuildToolsApiClasspath org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:2.1.10=kotlinBuildToolsApiClasspath org.jetbrains.kotlin:kotlin-scripting-jvm:2.1.10=kotlinBuildToolsApiClasspath +org.jetbrains.kotlin:kotlin-stdlib-common:1.5.0=compileOnlyDependenciesMetadata org.jetbrains.kotlin:kotlin-stdlib-common:1.8.21=_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-core -org.jetbrains.kotlin:kotlin-stdlib-common:1.8.22=compileOnlyDependenciesMetadata org.jetbrains.kotlin:kotlin-stdlib-common:1.9.0=_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-launcher org.jetbrains.kotlin:kotlin-stdlib-common:1.9.22=dokkaGfmPartialPlugin,dokkaGfmPartialRuntime,dokkaGfmPlugin,dokkaGfmRuntime,dokkaHtmlPartialPlugin,dokkaHtmlPartialRuntime,dokkaHtmlPlugin,dokkaHtmlRuntime,dokkaJavadocPartialPlugin,dokkaJavadocPartialRuntime,dokkaJavadocPlugin,dokkaJavadocRuntime,dokkaJekyllPartialPlugin,dokkaJekyllPartialRuntime,dokkaJekyllPlugin,dokkaJekyllRuntime org.jetbrains.kotlin:kotlin-stdlib-common:2.1.0=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle org.jetbrains.kotlin:kotlin-stdlib-common:2.1.10=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata +org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.0=compileOnlyDependenciesMetadata org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.20=_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-core,_internal-unified-test-platform-launcher,dokkaGfmPartialRuntime,dokkaGfmRuntime,dokkaHtmlPartialRuntime,dokkaHtmlRuntime,dokkaJavadocPartialRuntime,dokkaJavadocRuntime,dokkaJekyllPartialRuntime,dokkaJekyllRuntime,testImplementationDependenciesMetadata -org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.22=compileOnlyDependenciesMetadata org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0=dokkaGfmPartialPlugin,dokkaGfmPlugin,dokkaHtmlPartialPlugin,dokkaHtmlPlugin,dokkaJavadocPartialPlugin,dokkaJavadocPlugin,dokkaJekyllPartialPlugin,dokkaJekyllPlugin org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.1.0=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.1.10=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.0=compileOnlyDependenciesMetadata org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.20=_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-core,_internal-unified-test-platform-launcher,dokkaGfmPartialRuntime,dokkaGfmRuntime,dokkaHtmlPartialRuntime,dokkaHtmlRuntime,dokkaJavadocPartialRuntime,dokkaJavadocRuntime,dokkaJekyllPartialRuntime,dokkaJekyllRuntime,testImplementationDependenciesMetadata -org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22=compileOnlyDependenciesMetadata org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0=dokkaGfmPartialPlugin,dokkaGfmPlugin,dokkaHtmlPartialPlugin,dokkaHtmlPlugin,dokkaJavadocPartialPlugin,dokkaJavadocPlugin,dokkaJekyllPartialPlugin,dokkaJekyllPlugin org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.0=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.10=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath +org.jetbrains.kotlin:kotlin-stdlib:1.5.0=compileOnlyDependenciesMetadata org.jetbrains.kotlin:kotlin-stdlib:1.8.21=_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-core -org.jetbrains.kotlin:kotlin-stdlib:1.8.22=compileOnlyDependenciesMetadata org.jetbrains.kotlin:kotlin-stdlib:1.9.0=_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-launcher org.jetbrains.kotlin:kotlin-stdlib:1.9.22=dokkaGfmPartialPlugin,dokkaGfmPartialRuntime,dokkaGfmPlugin,dokkaGfmRuntime,dokkaHtmlPartialPlugin,dokkaHtmlPartialRuntime,dokkaHtmlPlugin,dokkaHtmlRuntime,dokkaJavadocPartialPlugin,dokkaJavadocPartialRuntime,dokkaJavadocPlugin,dokkaJavadocRuntime,dokkaJekyllPartialPlugin,dokkaJekyllPartialRuntime,dokkaJekyllPlugin,dokkaJekyllRuntime org.jetbrains.kotlin:kotlin-stdlib:2.1.0=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle @@ -262,21 +207,23 @@ org.jetbrains.kotlin:kotlin-stdlib:2.1.10=bcv-rt-jvm-cp-resolver,debugAndroidTes org.jetbrains.kotlin:kotlin-test-junit:2.1.10=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata org.jetbrains.kotlin:kotlin-test:2.1.10=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata org.jetbrains.kotlinx:atomicfu-jvm:0.22.0=_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-launcher -org.jetbrains.kotlinx:atomicfu:0.17.3=compileOnlyDependenciesMetadata,implementationDependenciesMetadata +org.jetbrains.kotlinx:atomicfu:0.16.1=compileOnlyDependenciesMetadata +org.jetbrains.kotlinx:atomicfu:0.17.3=implementationDependenciesMetadata org.jetbrains.kotlinx:atomicfu:0.20.2=testImplementationDependenciesMetadata org.jetbrains.kotlinx:atomicfu:0.22.0=_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-launcher -org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4=compileOnlyDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath +org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0=compileOnlyDependenciesMetadata +org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath -org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4=compileOnlyDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath +org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.1=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core,_internal-unified-test-platform-launcher,dokkaGfmPartialPlugin,dokkaGfmPartialRuntime,dokkaGfmPlugin,dokkaGfmRuntime,dokkaHtmlPartialPlugin,dokkaHtmlPartialRuntime,dokkaHtmlPlugin,dokkaHtmlRuntime,dokkaJavadocPartialPlugin,dokkaJavadocPartialRuntime,dokkaJavadocPlugin,dokkaJavadocRuntime,dokkaJekyllPartialPlugin,dokkaJekyllPartialRuntime,dokkaJekyllPlugin,dokkaJekyllRuntime org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath,releaseCompileClasspath,releaseRuntimeClasspath org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.1=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-driver-instrumentation,_internal-unified-test-platform-android-test-plugin,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core,_internal-unified-test-platform-launcher,dokkaGfmPartialPlugin,dokkaGfmPartialRuntime,dokkaGfmPlugin,dokkaGfmRuntime,dokkaHtmlPartialPlugin,dokkaHtmlPartialRuntime,dokkaHtmlPlugin,dokkaHtmlRuntime,dokkaJavadocPartialPlugin,dokkaJavadocPartialRuntime,dokkaJavadocPlugin,dokkaJavadocRuntime,dokkaJekyllPartialPlugin,dokkaJekyllPartialRuntime,dokkaJekyllPlugin,dokkaJekyllRuntime -org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4=compileOnlyDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath +org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0=compileOnlyDependenciesMetadata +org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3=_internal-unified-test-platform-android-device-provider-ddmlib,_internal-unified-test-platform-android-device-provider-gradle,_internal-unified-test-platform-android-test-plugin-host-additional-test-output,_internal-unified-test-platform-android-test-plugin-host-apk-installer,_internal-unified-test-platform-android-test-plugin-host-coverage,_internal-unified-test-platform-android-test-plugin-host-device-info,_internal-unified-test-platform-android-test-plugin-host-emulator-control,_internal-unified-test-platform-android-test-plugin-host-logcat,_internal-unified-test-platform-android-test-plugin-host-retention,_internal-unified-test-platform-android-test-plugin-result-listener-gradle,_internal-unified-test-platform-core,_internal-unified-test-platform-launcher,dokkaGfmPartialPlugin,dokkaGfmPartialRuntime,dokkaGfmPlugin,dokkaGfmRuntime,dokkaHtmlPartialPlugin,dokkaHtmlPartialRuntime,dokkaHtmlPlugin,dokkaHtmlRuntime,dokkaJavadocPartialPlugin,dokkaJavadocPartialRuntime,dokkaJavadocPlugin,dokkaJavadocRuntime,dokkaJekyllPartialPlugin,dokkaJekyllPartialRuntime,dokkaJekyllPlugin,dokkaJekyllRuntime -org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.4=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath org.jetbrains.kotlinx:kotlinx-html-jvm:0.9.1=dokkaGfmPartialPlugin,dokkaGfmPlugin,dokkaHtmlPartialPlugin,dokkaHtmlPlugin,dokkaJavadocPartialPlugin,dokkaJavadocPlugin,dokkaJekyllPartialPlugin,dokkaJekyllPlugin org.jetbrains.kotlinx:kover-jvm-agent:0.9.0=koverJvmAgent,koverJvmReporter org.jetbrains:annotations:13.0=bcv-rt-jvm-cp-resolver,compileOnlyDependenciesMetadata,debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath,releaseCompileClasspath,releaseRuntimeClasspath diff --git a/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt b/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt deleted file mode 100644 index f24fa9c5..00000000 --- a/posthog-android/src/main/java/com/posthog/android/PostHogPushNotifications.kt +++ /dev/null @@ -1,187 +0,0 @@ -package com.posthog.android - -import android.Manifest -import android.app.Activity -import android.content.pm.PackageManager -import android.os.Build -import androidx.activity.ComponentActivity -import androidx.activity.result.ActivityResultLauncher -import androidx.activity.result.contract.ActivityResultContracts -import androidx.core.content.ContextCompat -import com.posthog.PostHog - -/** - * Wraps an [ActivityResultLauncher] for the POST_NOTIFICATIONS permission and ties - * its result to FCM token registration with PostHog. Created by - * [PostHogPushNotifications.registerPermissionLauncher]; safe to invoke [launch] from - * any lifecycle state (e.g. button handlers) after the owning activity is created. - */ -public class PostHogPushPermissionLauncher internal constructor( - private val activity: Activity, - private val permissionLauncher: ActivityResultLauncher?, -) { - /** - * If permission is already granted (or not required), fetches the FCM token and - * registers it with PostHog immediately. Otherwise, prompts the user for - * POST_NOTIFICATIONS; on grant, the token is fetched and registered. - */ - public fun launch() { - if (PostHogPushNotifications.hasPermission(activity)) { - PostHogPushNotifications.fetchAndRegisterFcmToken(activity) - return - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - permissionLauncher?.launch(Manifest.permission.POST_NOTIFICATIONS) - } - } -} - -/** - * Utility class for handling push notification registration with PostHog. - * - * Requirements: - * - Firebase Messaging must be included in the app's dependencies - * - Firebase must be initialized in the app - * - * Usage: - * ```kotlin - * class MyActivity : ComponentActivity() { - * // Must be created before the activity reaches STARTED — a field initializer - * // or onCreate is fine. Do NOT call this from a button handler. - * private val pushLauncher = PostHogPushNotifications.registerPermissionLauncher(this) - * - * override fun onCreate(savedInstanceState: Bundle?) { - * super.onCreate(savedInstanceState) - * // Invoke from anywhere — button click, settings toggle, first launch, etc. - * pushLauncher.launch() - * } - * } - * ``` - */ -public object PostHogPushNotifications { - /** - * Registers an Activity Result launcher for the POST_NOTIFICATIONS permission. - * Must be called before the activity reaches STARTED (field initializer or onCreate). - * The returned [PostHogPushPermissionLauncher] can be invoked from any lifecycle - * state to prompt for permission (if needed) and register the FCM token. - * - * @param activity the ComponentActivity that owns the launcher - * @param onPermissionResult optional callback; receives `true` if permission was - * granted (or not required), `false` if denied - */ - @JvmStatic - @JvmOverloads - @Suppress("InvalidFragmentVersionForActivityResult") - public fun registerPermissionLauncher( - activity: ComponentActivity, - onPermissionResult: ((Boolean) -> Unit)? = null, - ): PostHogPushPermissionLauncher { - val launcher = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - activity.registerForActivityResult( - ActivityResultContracts.RequestPermission(), - ) { granted -> - if (granted) { - fetchAndRegisterFcmToken(activity) - } - onPermissionResult?.invoke(granted) - } - } else { - null - } - return PostHogPushPermissionLauncher(activity, launcher) - } - - /** - * Checks whether push notification permission has already been granted. - * - * @param activity the Activity to check permission against - * @return true if permission is granted or the device is below Android 13 - */ - @JvmStatic - public fun hasPermission(activity: Activity): Boolean { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - ContextCompat.checkSelfPermission( - activity, - Manifest.permission.POST_NOTIFICATIONS, - ) == PackageManager.PERMISSION_GRANTED - } else { - true - } - } - - /** - * Registers a push notification token directly with PostHog without requesting permission. - * Use this if you already have the FCM token (e.g. from FirebaseMessagingService.onNewToken). - * - * @param deviceToken the FCM device token - * @param firebaseProjectId the Firebase project ID (used as appId for the push subscription) - */ - @JvmStatic - public fun registerToken( - deviceToken: String, - firebaseProjectId: String, - ) { - PostHog.registerPushNotificationToken( - deviceToken = deviceToken, - appId = firebaseProjectId, - platform = "android", - ) - } - - internal fun fetchAndRegisterFcmToken(activity: Activity) { - try { - val firebaseMessaging = - Class.forName("com.google.firebase.messaging.FirebaseMessaging") - - val getInstance = firebaseMessaging.getMethod("getInstance") - val instance = getInstance.invoke(null) - - val getToken = firebaseMessaging.getMethod("getToken") - val task = getToken.invoke(instance) - - val firebaseApp = Class.forName("com.google.firebase.FirebaseApp") - val getFirebaseInstance = firebaseApp.getMethod("getInstance") - val appInstance = getFirebaseInstance.invoke(null) - val getOptions = firebaseApp.getMethod("getOptions") - val options = getOptions.invoke(appInstance) - - val firebaseOptions = Class.forName("com.google.firebase.FirebaseOptions") - val getProjectId = firebaseOptions.getMethod("getProjectId") - val projectId = getProjectId.invoke(options) as? String ?: "" - - val taskClass = Class.forName("com.google.android.gms.tasks.Task") - val addOnSuccessListenerMethod = - taskClass.getMethod( - "addOnSuccessListener", - Class.forName("com.google.android.gms.tasks.OnSuccessListener"), - ) - - val proxy = - java.lang.reflect.Proxy.newProxyInstance( - activity.classLoader, - arrayOf(Class.forName("com.google.android.gms.tasks.OnSuccessListener")), - ) { _, _, args -> - val token = args?.firstOrNull() as? String - if (!token.isNullOrBlank() && projectId.isNotBlank()) { - PostHog.registerPushNotificationToken( - deviceToken = token, - appId = projectId, - platform = "android", - ) - } - null - } - - addOnSuccessListenerMethod.invoke(task, proxy) - } catch (e: ClassNotFoundException) { - val config = PostHog.getConfig() - config?.logger?.log( - "Firebase Messaging is not available. Add firebase-messaging dependency to use push notifications.", - ) - } catch (e: Throwable) { - val config = PostHog.getConfig() - config?.logger?.log("Failed to fetch FCM token: $e.") - } - } -} diff --git a/posthog-android/src/test/java/com/posthog/android/PostHogPushNotificationsTest.kt b/posthog-android/src/test/java/com/posthog/android/PostHogPushNotificationsTest.kt deleted file mode 100644 index c6762e4e..00000000 --- a/posthog-android/src/test/java/com/posthog/android/PostHogPushNotificationsTest.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.posthog.android - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.posthog.PostHog -import org.junit.runner.RunWith -import kotlin.test.AfterTest -import kotlin.test.BeforeTest -import kotlin.test.Test -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -internal class PostHogPushNotificationsTest { - private lateinit var fake: PostHogFake - - @BeforeTest - fun `set up`() { - PostHog.close() - fake = createPostHogFake() - } - - @AfterTest - fun `set down`() { - PostHog.resetSharedInstance() - } - - @Test - fun `registerToken delegates to PostHog with android platform`() { - PostHogPushNotifications.registerToken( - deviceToken = "fcm-token", - firebaseProjectId = "firebase-project", - ) - - assertEquals(1, fake.pushRegistrations) - assertEquals("fcm-token", fake.pushDeviceToken) - assertEquals("firebase-project", fake.pushAppId) - assertEquals("android", fake.pushPlatform) - } -} diff --git a/posthog/src/main/java/com/posthog/PostHog.kt b/posthog/src/main/java/com/posthog/PostHog.kt index a1dd5ff1..c4b6245f 100644 --- a/posthog/src/main/java/com/posthog/PostHog.kt +++ b/posthog/src/main/java/com/posthog/PostHog.kt @@ -313,6 +313,7 @@ public class PostHog private constructor( queue?.stop() replayQueue?.stop() api = null + pushSubscriptionManager?.close() pushSubscriptionManager = null featureFlagsCalled.clear() diff --git a/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionManager.kt b/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionManager.kt index a0e49e37..f62d7647 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionManager.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionManager.kt @@ -168,6 +168,11 @@ internal class PostHogPushSubscriptionManager( } } + fun close() { + cancelTimer() + retryCount = 0 + } + private fun isRetryable(e: Throwable): Boolean { return when (e) { is PostHogApiError -> e.statusCode < 400 || e.statusCode in RETRYABLE_STATUS_CODES From bc1634a1ab5cd4ff09b6357b827cd1eda7bb8545 Mon Sep 17 00:00:00 2001 From: Haven Date: Wed, 29 Apr 2026 12:49:37 -0500 Subject: [PATCH 17/18] tweak --- posthog-android/build.gradle.kts | 2 +- posthog-android/gradle.lockfile | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/posthog-android/build.gradle.kts b/posthog-android/build.gradle.kts index d97a3787..3e3f4a6a 100644 --- a/posthog-android/build.gradle.kts +++ b/posthog-android/build.gradle.kts @@ -92,7 +92,7 @@ dependencies { implementation(kotlin("stdlib-jdk8", PosthogBuildConfig.Kotlin.KOTLIN)) implementation("androidx.lifecycle:lifecycle-process:${PosthogBuildConfig.Dependencies.LIFECYCLE}") implementation("androidx.lifecycle:lifecycle-common-java8:${PosthogBuildConfig.Dependencies.LIFECYCLE}") - implementation("androidx.core:core:${PosthogBuildConfig.Dependencies.ANDROIDX_CORE}") + implementation("androidx.core:core-ktx:${PosthogBuildConfig.Dependencies.ANDROIDX_CORE}") implementation("com.squareup.curtains:curtains:${PosthogBuildConfig.Dependencies.CURTAINS}") // compile only diff --git a/posthog-android/gradle.lockfile b/posthog-android/gradle.lockfile index 51fa7f24..d01b5b1c 100644 --- a/posthog-android/gradle.lockfile +++ b/posthog-android/gradle.lockfile @@ -17,6 +17,7 @@ androidx.compose.ui:ui-unit:1.0.0=compileOnlyDependenciesMetadata,debugCompileCl androidx.compose.ui:ui:1.0.0=compileOnlyDependenciesMetadata,debugCompileClasspath,releaseCompileClasspath androidx.concurrent:concurrent-futures-ktx:1.1.0=debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata androidx.concurrent:concurrent-futures:1.1.0=debugAndroidTestRuntimeClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath,testImplementationDependenciesMetadata +androidx.core:core-ktx:1.5.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath androidx.core:core:1.5.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath androidx.lifecycle:lifecycle-common-java8:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestRuntimeClasspath,implementationDependenciesMetadata,releaseCompileClasspath,releaseRuntimeClasspath,releaseUnitTestCompileClasspath,releaseUnitTestRuntimeClasspath androidx.lifecycle:lifecycle-common:2.3.1=testImplementationDependenciesMetadata From 3920629bcfe82502bd6a2936c1b4535ea0e312b8 Mon Sep 17 00:00:00 2001 From: Haven Date: Wed, 29 Apr 2026 12:55:21 -0500 Subject: [PATCH 18/18] fix linter --- .../java/com/posthog/android/internal/PostHogAndroidUtils.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog-android/src/main/java/com/posthog/android/internal/PostHogAndroidUtils.kt b/posthog-android/src/main/java/com/posthog/android/internal/PostHogAndroidUtils.kt index e5deb565..e46ea606 100644 --- a/posthog-android/src/main/java/com/posthog/android/internal/PostHogAndroidUtils.kt +++ b/posthog-android/src/main/java/com/posthog/android/internal/PostHogAndroidUtils.kt @@ -14,12 +14,12 @@ import android.graphics.Point import android.net.ConnectivityManager import android.net.Uri import android.os.Build -import androidx.core.net.toUri import android.os.Process import android.telephony.TelephonyManager import android.util.Base64 import android.util.DisplayMetrics import android.view.WindowManager +import androidx.core.net.toUri import com.posthog.PostHogInternal import com.posthog.android.PostHogAndroidConfig import java.io.ByteArrayOutputStream