Skip to content

Commit be72ad3

Browse files
oschwaldclaude
andcommitted
feat: Add WebView user agent collection
Collect the default WebView user agent string via WebSettings, which provides browser version and system information useful for device fingerprinting. Gracefully handles devices where WebView is unavailable. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent aba3ff8 commit be72ad3

File tree

3 files changed

+83
-1
lines changed

3 files changed

+83
-1
lines changed

device-sdk/src/main/java/com/maxmind/device/collector/DeviceDataCollector.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ internal class DeviceDataCollector(private val context: Context) {
3535
private val networkCollector = NetworkCollector(context)
3636
private val settingsCollector = SettingsCollector(context)
3737
private val behaviorCollector = BehaviorCollector(context)
38+
private val webViewCollector = WebViewCollector(context)
3839

3940
/**
4041
* Collects current device data.
@@ -61,7 +62,7 @@ internal class DeviceDataCollector(private val context: Context) {
6162
// Timezone offset in minutes
6263
timezoneOffset = TimeZone.getDefault().rawOffset / 60000,
6364
deviceTime = System.currentTimeMillis(),
64-
// Other fields will be populated by dedicated collectors in future commits
65+
webViewUserAgent = webViewCollector.collectUserAgent(),
6566
)
6667

6768
private fun collectBuildInfo(): BuildInfo =
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.maxmind.device.collector
2+
3+
import android.content.Context
4+
import android.webkit.WebSettings
5+
6+
/**
7+
* Collects WebView context information.
8+
*
9+
* Retrieves the default WebView user agent string which provides
10+
* browser and system version information.
11+
*/
12+
internal class WebViewCollector(
13+
private val context: Context,
14+
) {
15+
/**
16+
* Collects the default WebView user agent.
17+
*
18+
* @return User agent string, or null if unavailable
19+
*/
20+
fun collectUserAgent(): String? =
21+
try {
22+
WebSettings.getDefaultUserAgent(context)
23+
} catch (
24+
@Suppress("TooGenericExceptionCaught", "SwallowedException")
25+
e: Exception,
26+
) {
27+
// WebView may not be available on all devices
28+
null
29+
}
30+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.maxmind.device.collector
2+
3+
import android.content.Context
4+
import android.webkit.WebSettings
5+
import io.mockk.every
6+
import io.mockk.mockk
7+
import io.mockk.mockkStatic
8+
import io.mockk.unmockkStatic
9+
import org.junit.jupiter.api.AfterEach
10+
import org.junit.jupiter.api.Assertions.assertEquals
11+
import org.junit.jupiter.api.Assertions.assertNull
12+
import org.junit.jupiter.api.BeforeEach
13+
import org.junit.jupiter.api.Test
14+
15+
internal class WebViewCollectorTest {
16+
private lateinit var mockContext: Context
17+
private lateinit var collector: WebViewCollector
18+
19+
@BeforeEach
20+
internal fun setUp() {
21+
mockContext = mockk(relaxed = true)
22+
mockkStatic(WebSettings::class)
23+
collector = WebViewCollector(mockContext)
24+
}
25+
26+
@AfterEach
27+
internal fun tearDown() {
28+
unmockkStatic(WebSettings::class)
29+
}
30+
31+
@Test
32+
internal fun `collectUserAgent returns user agent string`() {
33+
val expectedUserAgent =
34+
"Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 " +
35+
"(KHTML, like Gecko) Chrome/119.0.0.0 Mobile Safari/537.36"
36+
every { WebSettings.getDefaultUserAgent(mockContext) } returns expectedUserAgent
37+
38+
val result = collector.collectUserAgent()
39+
40+
assertEquals(expectedUserAgent, result)
41+
}
42+
43+
@Test
44+
internal fun `collectUserAgent returns null when WebView unavailable`() {
45+
every { WebSettings.getDefaultUserAgent(mockContext) } throws RuntimeException("No WebView")
46+
47+
val result = collector.collectUserAgent()
48+
49+
assertNull(result)
50+
}
51+
}

0 commit comments

Comments
 (0)