package com.tt.honeyDue.analytics import android.app.Application import android.util.Log import com.posthog.PostHog import com.posthog.android.PostHogAndroid import com.posthog.android.PostHogAndroidConfig /** * Android implementation of PostHog Analytics. */ actual object PostHogAnalytics { // TODO: Replace with your actual PostHog API key private const val API_KEY = "YOUR_POSTHOG_API_KEY" private const val HOST = "https://us.i.posthog.com" private const val TAG = "PostHogAnalytics" private var isInitialized = false private var application: Application? = null /** * Initialize PostHog SDK with Application context. * Call this in MainActivity.onCreate() before using other methods. */ fun initialize(application: Application, debug: Boolean = false) { if (isInitialized) return this.application = application val config = PostHogAndroidConfig(API_KEY, HOST).apply { captureScreenViews = false // We'll track screens manually captureApplicationLifecycleEvents = true captureDeepLinks = true this.debug = debug // Session Replay sessionReplay = true sessionReplayConfig.maskAllTextInputs = true sessionReplayConfig.maskAllImages = false } PostHogAndroid.setup(application, config) isInitialized = true } /** * Initialize from common code (no-op on Android, use initialize(Application) instead) */ actual fun initialize() { // No-op - Android requires Application context, use initialize(Application) instead } actual fun identify(userId: String, properties: Map?) { if (!isInitialized) return PostHog.identify(userId, userProperties = properties) } actual fun capture(event: String, properties: Map?) { if (!isInitialized) return PostHog.capture(event, properties = properties) } /** * Capture an exception/crash as a PostHog `$exception` event. * Uses PostHog's standard exception property names so exceptions * appear correctly in the PostHog Errors dashboard. */ actual fun captureException(throwable: Throwable, properties: Map?) { if (!isInitialized) return try { val exceptionProps = mutableMapOf( "\$exception_type" to (throwable::class.simpleName ?: "Unknown"), "\$exception_message" to (throwable.message ?: "No message"), "\$exception_stack_trace_raw" to throwable.stackTraceToString() ) if (properties != null) { exceptionProps.putAll(properties) } PostHog.capture("\$exception", properties = exceptionProps) } catch (e: Exception) { Log.e(TAG, "Failed to capture exception to PostHog", e) } } actual fun screen(screenName: String, properties: Map?) { if (!isInitialized) return PostHog.screen(screenName, properties = properties) } actual fun reset() { if (!isInitialized) return PostHog.reset() } actual fun flush() { if (!isInitialized) return PostHog.flush() } /** * Install an uncaught exception handler that captures crashes to PostHog * before delegating to the default handler (which shows the crash dialog). * Call this after initialize() in MainActivity.onCreate(). */ actual fun setupExceptionHandler() { if (!isInitialized) return val defaultHandler = Thread.getDefaultUncaughtExceptionHandler() Thread.setDefaultUncaughtExceptionHandler { thread, throwable -> try { PostHog.capture( event = "\$exception", properties = mapOf( "\$exception_type" to (throwable::class.simpleName ?: "Unknown"), "\$exception_message" to (throwable.message ?: "No message"), "\$exception_stack_trace_raw" to throwable.stackTraceToString(), "\$exception_thread" to thread.name, "\$exception_is_fatal" to true ) ) // Flush to ensure the event is sent before the process dies PostHog.flush() } catch (_: Exception) { // Don't let our crash handler crash } // Call the default handler so the system crash dialog still appears defaultHandler?.uncaughtException(thread, throwable) } Log.d(TAG, "Uncaught exception handler installed") } }