Skip to content

Commit

Permalink
add referrer url automatically (#186)
Browse files Browse the repository at this point in the history
  • Loading branch information
beradeep authored Oct 2, 2024
1 parent 384a1c0 commit 8e044e4
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 10 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Next

- feat: add referrerURL automatically ([#186](https://github.com/PostHog/posthog-android/pull/186))

## 3.7.5 - 2024-09-26

- fix: isFeatureEnabled wasn't sending the $feature_flag_called event ([#185](https://github.com/PostHog/posthog-android/pull/185))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,23 @@ internal class PostHogActivityLifecycleCallbackIntegration(
savedInstanceState: Bundle?,
) {
if (config.captureDeepLinks) {
activity.intent.data?.let {
activity.intent?.let { intent ->
val props = mutableMapOf<String, Any>()
val data = intent.data
try {
for (item in it.queryParameterNames) {
val param = it.getQueryParameter(item)
if (!param.isNullOrEmpty()) {
props[item] = param
data?.let {
for (item in it.queryParameterNames) {
val param = it.getQueryParameter(item)
if (!param.isNullOrEmpty()) {
props[item] = param
}
}
}
} catch (e: UnsupportedOperationException) {
config.logger.log("Deep link $it has invalid query param names.")
config.logger.log("Deep link $data has invalid query param names.")
} finally {
props["url"] = it.toString()
data?.let { props["url"] = it.toString() }
intent.getReferrerInfo(config).let { props.putAll(it) }
PostHog.capture("Deep Link Opened", properties = props)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.GET_META_DATA
import android.graphics.Point
import android.net.ConnectivityManager
import android.net.Uri
import android.os.Build
import android.os.Process
import android.telephony.TelephonyManager
Expand Down Expand Up @@ -99,7 +101,11 @@ internal fun Context.appContext(): Context {
}

internal fun Context.hasPermission(permission: String): Boolean {
return checkPermission(permission, Process.myPid(), Process.myUid()) == PackageManager.PERMISSION_GRANTED
return checkPermission(
permission,
Process.myPid(),
Process.myUid(),
) == PackageManager.PERMISSION_GRANTED
}

@Suppress("DEPRECATION")
Expand Down Expand Up @@ -144,6 +150,39 @@ internal fun Activity.activityLabelOrName(config: PostHogAndroidConfig): String?
}
}

@Suppress("DEPRECATION")
internal fun Intent.getReferrerInfo(config: PostHogAndroidConfig): Map<String, String> {
val referrerInfoMap = mutableMapOf<String, String>()
val referrer: Uri?
val referrerName: String?
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
referrer = this.getParcelableExtra(Intent.EXTRA_REFERRER)
referrerName = this.getStringExtra(Intent.EXTRA_REFERRER_NAME)
} else {
referrer = this.getParcelableExtra("android.intent.extra.REFERRER")
referrerName = this.getStringExtra("android.intent.extra.REFERRER_NAME")
}
referrer?.let {
referrerInfoMap["\$referrer"] = referrer.toString()
referrer.host?.let { referrerInfoMap["\$referring_domain"] = it }
} ?: referrerName?.let {
referrerInfoMap["\$referrer"] = referrerName
referrerName.tryParse(config)?.let { uri ->
uri.host?.let { referrerInfoMap["\$referring_domain"] = it }
}
}
return referrerInfoMap
}

internal fun String.tryParse(config: PostHogAndroidConfig): Uri? {
return try {
Uri.parse(this)
} catch (e: Throwable) {
config.logger.log("Error parsing string: $this. Exception: $e.")
null
}
}

internal fun isMainThread(mainHandler: MainHandler): Boolean {
return Thread.currentThread().id == mainHandler.mainLooper.thread.id
}
16 changes: 14 additions & 2 deletions posthog-android/src/test/java/com/posthog/android/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,18 @@ import org.mockito.kotlin.whenever

public const val API_KEY: String = "_6SG-F7I1vCuZ-HdJL3VZQqjBlaSb1_20hDPwqMNnGI"

public fun mockActivityUri(uri: String): Activity {
public fun mockActivityUri(
uri: String,
referrer: Boolean = false,
): Activity {
val activity = mock<Activity>()
val intent =
Intent().apply {
data = Uri.parse(uri)
if (referrer) {
putExtra(Intent.EXTRA_REFERRER, Uri.parse(uri))
putExtra(Intent.EXTRA_REFERRER_NAME, uri)
}
}
whenever(activity.intent).thenReturn(intent)
return activity
Expand All @@ -55,7 +62,12 @@ public fun mockScreenTitle(
whenever(appInfo.loadLabel(any())).thenReturn(applicationLabel)

if (throws) {
whenever(pm.getActivityInfo(any(), any<Int>())).thenThrow(PackageManager.NameNotFoundException())
whenever(
pm.getActivityInfo(
any(),
any<Int>(),
),
).thenThrow(PackageManager.NameNotFoundException())
} else {
whenever(pm.getActivityInfo(any(), any<Int>())).thenReturn(ac)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,20 @@ internal class PostHogActivityLifecycleCallbackIntegrationTest {
return fake
}

private fun executeDeepLinkTestWithReferrer(
url: String,
captureDeepLinks: Boolean = true,
): PostHogFake {
val sut = getSut(captureDeepLinks = captureDeepLinks)
val activity = mockActivityUri(url, true)

val fake = createPostHogFake()

sut.install()
sut.onActivityCreated(activity, null)
return fake
}

@Test
fun `onActivityCreated captures deep link with url`() {
val url = "http://google.com"
Expand All @@ -96,6 +110,37 @@ internal class PostHogActivityLifecycleCallbackIntegrationTest {
assertEquals(url, fake.properties?.get("url"))
}

@Test
fun `onActivityCreated captures deep link with web referrer`() {
val url = "http://google.com"
val domain = url.substringAfter("://")
val fake = executeDeepLinkTestWithReferrer(url)

assertEquals("Deep Link Opened", fake.event)
assertEquals(url, fake.properties?.get("\$referrer"))
assertEquals(domain, fake.properties?.get("\$referring_domain"))
}

@Test
fun `onActivityCreated captures deep link with app referrer`() {
val url = "android-app://com.example.source"
val domain = url.substringAfter("://")
val fake = executeDeepLinkTestWithReferrer(url)

assertEquals("Deep Link Opened", fake.event)
assertEquals(url, fake.properties?.get("\$referrer"))
assertEquals(domain, fake.properties?.get("\$referring_domain"))
}

@Test
fun `onActivityCreated also captures referrer for unparsable url`() {
val url = "google.com"
val fake = executeDeepLinkTestWithReferrer(url)

assertEquals("Deep Link Opened", fake.event)
assertEquals(url, fake.properties?.get("\$referrer"))
}

@Test
fun `onActivityCreated does not capture deep link if disabled`() {
val fake = executeDeepLinkTest("http://google.com", captureDeepLinks = false)
Expand Down

0 comments on commit 8e044e4

Please sign in to comment.