Skip to content

Commit

Permalink
Merge pull request #7323 from vector-im/feature/mna/device-manager-pa…
Browse files Browse the repository at this point in the history
…rsing-os

[Device management] Improve the parsing for OS of Desktop/Web sessions (PSG-823)
  • Loading branch information
mnaturel authored Oct 13, 2022
2 parents 963c0e5 + daa5f88 commit f9eb6a6
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 37 deletions.
1 change: 1 addition & 0 deletions changelog.d/7321.wip
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[Device management] Improve the parsing for OS of Desktop/Web sessions
Original file line number Diff line number Diff line change
Expand Up @@ -74,55 +74,77 @@ class ParseDeviceUserAgentUseCase @Inject constructor() {
}

private fun parseDesktopUserAgent(userAgent: String): DeviceExtendedInfo {
val browserInfo = parseBrowserInfoFromDesktopUserAgent(userAgent)
val operatingSystem = parseOperatingSystemFromDesktopUserAgent(userAgent)

return DeviceExtendedInfo(
deviceType = DeviceType.DESKTOP,
deviceModel = null,
deviceOperatingSystem = operatingSystem,
clientName = browserInfo.name,
clientVersion = browserInfo.version,
)
}

private data class BrowserInfo(val name: String? = null, val version: String? = null)

private fun parseBrowserInfoFromDesktopUserAgent(userAgent: String): BrowserInfo {
val browserSegments = userAgent.split(" ")
val (browserName, browserVersion) = when {
return when {
isFirefox(browserSegments) -> {
Pair("Firefox", getBrowserVersion(browserSegments, "Firefox"))
BrowserInfo(BROWSER_FIREFOX, getBrowserVersion(browserSegments, BROWSER_FIREFOX))
}
isEdge(browserSegments) -> {
Pair("Edge", getBrowserVersion(browserSegments, "Edge"))
BrowserInfo(BROWSER_EDGE, getBrowserVersion(browserSegments, BROWSER_EDGE))
}
isMobile(browserSegments) -> {
when (val name = getMobileBrowserName(browserSegments)) {
null -> {
Pair(null, null)
BrowserInfo()
}
"Safari" -> {
Pair(name, getBrowserVersion(browserSegments, "Version"))
BROWSER_SAFARI -> {
BrowserInfo(name, getBrowserVersion(browserSegments, "Version"))
}
else -> {
Pair(name, getBrowserVersion(browserSegments, name))
BrowserInfo(name, getBrowserVersion(browserSegments, name))
}
}
}
isSafari(browserSegments) -> {
Pair("Safari", getBrowserVersion(browserSegments, "Version"))
BrowserInfo(BROWSER_SAFARI, getBrowserVersion(browserSegments, "Version"))
}
else -> {
when (val name = getRegularBrowserName(browserSegments)) {
null -> {
Pair(null, null)
BrowserInfo()
}
else -> {
Pair(name, getBrowserVersion(browserSegments, name))
BrowserInfo(name, getBrowserVersion(browserSegments, name))
}
}
}
}
}

val deviceOperatingSystemSegments = userAgent.substringAfter("(").substringBefore(")").split("; ")
val deviceOperatingSystem = if (deviceOperatingSystemSegments.getOrNull(1)?.startsWith("Android").orFalse()) {
deviceOperatingSystemSegments.getOrNull(1)
} else {
deviceOperatingSystemSegments.getOrNull(0)
private fun parseOperatingSystemFromDesktopUserAgent(userAgent: String): String? {
val deviceOperatingSystemSegments = userAgent
.substringAfter("(")
.substringBefore(")")
.split("; ")
val firstSegment = deviceOperatingSystemSegments.getOrNull(0).orEmpty()
val secondSegment = deviceOperatingSystemSegments.getOrNull(1).orEmpty()

return when {
// e.g. (Macintosh; Intel Mac OS X 10_15_7) => macOS
firstSegment.startsWith(OPERATING_SYSTEM_MAC_KEYWORD) -> OPERATING_SYSTEM_MAC
// e.g. (Windows NT 10.0; Win64; x64) => Windows
firstSegment.startsWith(OPERATING_SYSTEM_WINDOWS_KEYWORD) -> OPERATING_SYSTEM_WINDOWS_KEYWORD
// e.g. (iPad; CPU OS 8_4_1 like Mac OS X) => iOS
firstSegment.startsWith(DEVICE_IPAD_KEYWORD) || firstSegment.startsWith(DEVICE_IPHONE_KEYWORD) -> OPERATING_SYSTEM_IOS
// e.g. (Linux; Android 9; SM-G973U Build/PPR1.180610.011) => Android
secondSegment.startsWith(OPERATING_SYSTEM_ANDROID_KEYWORD) -> OPERATING_SYSTEM_ANDROID_KEYWORD
else -> null
}
return DeviceExtendedInfo(
deviceType = DeviceType.DESKTOP,
deviceModel = null,
deviceOperatingSystem = deviceOperatingSystem,
clientName = browserName,
clientVersion = browserVersion,
)
}

private fun parseWebUserAgent(userAgent: String): DeviceExtendedInfo {
Expand All @@ -136,7 +158,7 @@ class ParseDeviceUserAgentUseCase @Inject constructor() {
}

private fun isFirefox(browserSegments: List<String>): Boolean {
return browserSegments.lastOrNull()?.startsWith("Firefox").orFalse()
return browserSegments.lastOrNull()?.startsWith(BROWSER_FIREFOX).orFalse()
}

private fun getBrowserVersion(browserSegments: List<String>, browserName: String): String? {
Expand All @@ -148,11 +170,11 @@ class ParseDeviceUserAgentUseCase @Inject constructor() {
}

private fun isEdge(browserSegments: List<String>): Boolean {
return browserSegments.lastOrNull()?.startsWith("Edge").orFalse()
return browserSegments.lastOrNull()?.startsWith(BROWSER_EDGE).orFalse()
}

private fun isSafari(browserSegments: List<String>): Boolean {
return browserSegments.lastOrNull()?.startsWith("Safari").orFalse() &&
return browserSegments.lastOrNull()?.startsWith(BROWSER_SAFARI).orFalse() &&
browserSegments.getOrNull(browserSegments.size - 2)?.startsWith("Version").orFalse()
}

Expand All @@ -163,7 +185,7 @@ class ParseDeviceUserAgentUseCase @Inject constructor() {
private fun getMobileBrowserName(browserSegments: List<String>): String? {
val possibleBrowserName = browserSegments.getOrNull(browserSegments.size - 3)?.split("/")?.firstOrNull()
return if (possibleBrowserName == "Version") {
"Safari"
BROWSER_SAFARI
} else {
possibleBrowserName
}
Expand All @@ -187,5 +209,17 @@ class ParseDeviceUserAgentUseCase @Inject constructor() {

// Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
private const val WEB_KEYWORD = "Mozilla/"

private const val OPERATING_SYSTEM_MAC_KEYWORD = "Macintosh"
private const val OPERATING_SYSTEM_MAC = "macOS"
private const val OPERATING_SYSTEM_IOS = "iOS"
private const val OPERATING_SYSTEM_WINDOWS_KEYWORD = "Windows"
private const val OPERATING_SYSTEM_ANDROID_KEYWORD = "Android"
private const val DEVICE_IPAD_KEYWORD = "iPad"
private const val DEVICE_IPHONE_KEYWORD = "iPhone"

private const val BROWSER_FIREFOX = "Firefox"
private const val BROWSER_SAFARI = "Safari"
private const val BROWSER_EDGE = "Edge"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ private val A_USER_AGENT_LIST_FOR_DESKTOP = listOf(
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) ElementNightly/2022091301 Chrome/104.0.5112.102 Electron/20.1.1 Safari/537.36",
)
private val AN_EXPECTED_RESULT_LIST_FOR_DESKTOP = listOf(
DeviceExtendedInfo(DeviceType.DESKTOP, null, "Macintosh", "Electron", "20.1.1"),
DeviceExtendedInfo(DeviceType.DESKTOP, null, "Windows NT 10.0", "Electron", "20.1.1"),
DeviceExtendedInfo(DeviceType.DESKTOP, null, "macOS", "Electron", "20.1.1"),
DeviceExtendedInfo(DeviceType.DESKTOP, null, "Windows", "Electron", "20.1.1"),
)

private val A_USER_AGENT_LIST_FOR_WEB = listOf(
Expand All @@ -78,15 +78,15 @@ private val A_USER_AGENT_LIST_FOR_WEB = listOf(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246",
)
private val AN_EXPECTED_RESULT_LIST_FOR_WEB = listOf(
DeviceExtendedInfo(DeviceType.WEB, null, "Macintosh", "Chrome", "104.0.5112.102"),
DeviceExtendedInfo(DeviceType.WEB, null, "Windows NT 10.0", "Chrome", "104.0.5112.102"),
DeviceExtendedInfo(DeviceType.WEB, null, "Macintosh", "Firefox", "39.0"),
DeviceExtendedInfo(DeviceType.WEB, null, "Macintosh", "Safari", "8.0.3"),
DeviceExtendedInfo(DeviceType.WEB, null, "Android 9", "Chrome", "69.0.3497.100"),
DeviceExtendedInfo(DeviceType.WEB, null, "iPad", "Safari", "8.0"),
DeviceExtendedInfo(DeviceType.WEB, null, "iPhone", "Safari", "8.0"),
DeviceExtendedInfo(DeviceType.WEB, null, "Windows NT 6.0", "Firefox", "40.0"),
DeviceExtendedInfo(DeviceType.WEB, null, "Windows NT 10.0", "Edge", "12.246"),
DeviceExtendedInfo(DeviceType.WEB, null, "macOS", "Chrome", "104.0.5112.102"),
DeviceExtendedInfo(DeviceType.WEB, null, "Windows", "Chrome", "104.0.5112.102"),
DeviceExtendedInfo(DeviceType.WEB, null, "macOS", "Firefox", "39.0"),
DeviceExtendedInfo(DeviceType.WEB, null, "macOS", "Safari", "8.0.3"),
DeviceExtendedInfo(DeviceType.WEB, null, "Android", "Chrome", "69.0.3497.100"),
DeviceExtendedInfo(DeviceType.WEB, null, "iOS", "Safari", "8.0"),
DeviceExtendedInfo(DeviceType.WEB, null, "iOS", "Safari", "8.0"),
DeviceExtendedInfo(DeviceType.WEB, null, "Windows", "Firefox", "40.0"),
DeviceExtendedInfo(DeviceType.WEB, null, "Windows", "Edge", "12.246"),
)

private val AN_UNKNOWN_USER_AGENT_LIST = listOf(
Expand Down

0 comments on commit f9eb6a6

Please sign in to comment.