From c70b6206d0a3394cf4a874967b83c87f31daff5d Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 26 Sep 2022 14:39:23 +0300 Subject: [PATCH 01/16] Add user agent into device info. --- .../api/session/crypto/model/DeviceInfo.kt | 10 +++- .../settings/devices/v2/DeviceFullInfo.kt | 1 + .../settings/devices/v2/DeviceUserAgent.kt | 46 +++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/DeviceInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/DeviceInfo.kt index b144069b99..338a594011 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/DeviceInfo.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/DeviceInfo.kt @@ -52,9 +52,17 @@ data class DeviceInfo( * The last ip address. */ @Json(name = "last_seen_ip") - val lastSeenIp: String? = null + val lastSeenIp: String? = null, + + @Json(name="org.matrix.msc3852.last_seen_user_agent") + val unstableLastSeenUserAgent: String? = null, + + @Json(name="last_seen_user_agent") + val lastSeenUserAgent: String? = null, ) : DatedObject { override val date: Long get() = lastSeenTs ?: 0 + + fun getBestLastSeenUserAgent() = lastSeenUserAgent ?: unstableLastSeenUserAgent } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceFullInfo.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceFullInfo.kt index 373df53b1b..a47a9340c3 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceFullInfo.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceFullInfo.kt @@ -26,4 +26,5 @@ data class DeviceFullInfo( val roomEncryptionTrustLevel: RoomEncryptionTrustLevel, val isInactive: Boolean, val isCurrentDevice: Boolean, + val deviceUserAgent: DeviceUserAgent, ) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt new file mode 100644 index 0000000000..14b02ce073 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2 + +import im.vector.app.features.settings.devices.v2.list.DeviceType + +data class DeviceUserAgent( + /** + * One of MOBILE, WEB, DESKTOP or UNKNOWN + */ + val deviceType: DeviceType, + /** + * i.e. Google Pixel 6 + */ + val deviceModel: String?, + /** + * i.e. Android + */ + val deviceOperatingSystem: String?, + /** + * i.e. Android 11 + */ + val deviceOperatingSystemVersion: String?, + /** + * i.e. Element Nightly + */ + val clientName: String?, + /** + * i.e. 1.5.0 + */ + val clientVersion: String?, +) From 3e66a6538ee4e43cbe03876c22d9b0810361adf6 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Tue, 27 Sep 2022 15:45:34 +0300 Subject: [PATCH 02/16] Implement use case to parse user agent. --- .../settings/devices/v2/DeviceUserAgent.kt | 20 +++-- .../devices/v2/ParseDeviceUserAgentUseCase.kt | 90 +++++++++++++++++++ 2 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt index 14b02ce073..f307025d0b 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt @@ -24,23 +24,31 @@ data class DeviceUserAgent( */ val deviceType: DeviceType, /** - * i.e. Google Pixel 6 + * i.e. Google */ - val deviceModel: String?, + val deviceManufacturer: String? = null, + /** + * i.e. Pixel 6 + */ + val deviceModel: String? = null, /** * i.e. Android */ - val deviceOperatingSystem: String?, + val deviceOperatingSystem: String? = null, /** * i.e. Android 11 */ - val deviceOperatingSystemVersion: String?, + val deviceOperatingSystemVersion: String? = null, /** * i.e. Element Nightly */ - val clientName: String?, + val clientName: String? = null, /** * i.e. 1.5.0 */ - val clientVersion: String?, + val clientVersion: String? = null, + /** + * i.e. Chrome + */ + val browser: String? = null, ) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt new file mode 100644 index 0000000000..1ea7dabbe4 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2 + +import im.vector.app.features.settings.devices.v2.list.DeviceType +import javax.inject.Inject + +class ParseDeviceUserAgentUseCase @Inject constructor() { + + fun execute(userAgent: String): DeviceUserAgent { + return when { + userAgent.contains(ANDROID_KEYWORD) -> parseAndroidUserAgent(userAgent) + userAgent.contains(IOS_KEYWORD) -> parseIosUserAgent(userAgent) + userAgent.contains(DESKTOP_KEYWORD) -> parseDesktopUserAgent(userAgent) + userAgent.contains(WEB_KEYWORD) -> parseWebUserAgent(userAgent) + else -> createUnknownUserAgent() + } + } + + private fun parseAndroidUserAgent(userAgent: String): DeviceUserAgent { + val appName = userAgent.substringBefore("/") + val appVersion = userAgent.substringAfter("/").substringBefore(" (") + val deviceInfoSegments = userAgent.substringAfter("(").substringBefore(")").split("; ") + val deviceManufacturer = deviceInfoSegments.getOrNull(0) + val deviceModel = deviceInfoSegments.getOrNull(1) + val deviceOsInfo = deviceInfoSegments.getOrNull(2)?.takeIf { it.startsWith("Android") } + val deviceOs = deviceOsInfo?.substringBefore(" ") + val deviceOsVersion = deviceOsInfo?.substringAfter(" ") + return DeviceUserAgent(DeviceType.MOBILE, deviceManufacturer, deviceModel, deviceOs, deviceOsVersion, appName, appVersion) + } + + private fun parseIosUserAgent(userAgent: String): DeviceUserAgent { + val appName = userAgent.substringBefore("/") + val appVersion = userAgent.substringAfter("/").substringBefore(" (") + val deviceInfoSegments = userAgent.substringAfter("(").substringBefore(")").split("; ") + val deviceManufacturer = "Apple" + val deviceModel = deviceInfoSegments.getOrNull(0) + val deviceOs = deviceInfoSegments.getOrNull(1)?.substringBefore(" ") + val deviceOsVersion = deviceInfoSegments.getOrNull(1)?.substringAfter(" ") + return DeviceUserAgent(DeviceType.MOBILE, deviceManufacturer, deviceModel, deviceOs, deviceOsVersion, appName, appVersion) + } + + private fun parseDesktopUserAgent(userAgent: String): DeviceUserAgent { + val appInfoSegments = userAgent.substringBeforeLast(" ").substringAfterLast(" ").split("/") + val appName = appInfoSegments.getOrNull(0) + val appVersion = appInfoSegments.getOrNull(1) + val deviceInfoSegments = userAgent.substringAfter("(").substringBefore(")").split("; ") + val deviceOs = deviceInfoSegments.getOrNull(1)?.substringBeforeLast(" ") + val deviceOsVersion = deviceInfoSegments.getOrNull(1)?.substringAfterLast(" ") + return DeviceUserAgent(DeviceType.DESKTOP, null, null, deviceOs, deviceOsVersion, appName, appVersion) + } + + private fun parseWebUserAgent(userAgent: String): DeviceUserAgent { + return parseDesktopUserAgent(userAgent).copy( + deviceType = DeviceType.WEB + ) + } + + private fun createUnknownUserAgent(): DeviceUserAgent { + return DeviceUserAgent(DeviceType.UNKNOWN) + } + + companion object { + // Element dbg/1.5.0-dev (Xiaomi; Mi 9T; Android 11; RKQ1.200826.002 test-keys; Flavour GooglePlay; MatrixAndroidSdk2 1.5.0) + private val ANDROID_KEYWORD = "; MatrixAndroidSdk2" + + // Element/1.8.21 (iPhone XS Max; iOS 15.2; Scale/3.00) + private val IOS_KEYWORD = "; iOS " + + // Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) ElementNightly/2022091301 Chrome/104.0.5112.102 Electron/20.1.1 Safari/537.36 + private val DESKTOP_KEYWORD = " Electron/" + + // 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 val WEB_KEYWORD = "Mozilla/" + } +} From 04a305b40329d27af031d7a0af0f982943bc8f28 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 28 Sep 2022 14:32:20 +0300 Subject: [PATCH 03/16] Refactor user agent parsing. --- .../settings/devices/v2/DeviceUserAgent.kt | 12 ++----- .../v2/GetDeviceFullInfoListUseCase.kt | 4 ++- .../devices/v2/ParseDeviceUserAgentUseCase.kt | 33 +++++++++++-------- .../v2/overview/GetDeviceFullInfoUseCase.kt | 5 +++ .../devices/v2/DevicesViewModelTest.kt | 8 +++-- .../v2/GetDeviceFullInfoListUseCaseTest.kt | 12 +++++-- .../v2/filter/FilterDevicesUseCaseTest.kt | 14 +++++--- .../overview/GetDeviceFullInfoUseCaseTest.kt | 8 ++++- 8 files changed, 61 insertions(+), 35 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt index f307025d0b..cf201fafa4 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt @@ -24,21 +24,13 @@ data class DeviceUserAgent( */ val deviceType: DeviceType, /** - * i.e. Google - */ - val deviceManufacturer: String? = null, - /** - * i.e. Pixel 6 + * i.e. Google Pixel 6 */ val deviceModel: String? = null, - /** - * i.e. Android - */ - val deviceOperatingSystem: String? = null, /** * i.e. Android 11 */ - val deviceOperatingSystemVersion: String? = null, + val deviceOperatingSystem: String? = null, /** * i.e. Element Nightly */ diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt index b2341e23f7..e4cdbb0a87 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt @@ -38,6 +38,7 @@ class GetDeviceFullInfoListUseCase @Inject constructor( private val getEncryptionTrustLevelForDeviceUseCase: GetEncryptionTrustLevelForDeviceUseCase, private val getCurrentSessionCrossSigningInfoUseCase: GetCurrentSessionCrossSigningInfoUseCase, private val filterDevicesUseCase: FilterDevicesUseCase, + private val parseDeviceUserAgentUseCase: ParseDeviceUserAgentUseCase, ) { fun execute(filterType: DeviceManagerFilterType, excludeCurrentDevice: Boolean = false): Flow> { @@ -72,7 +73,8 @@ class GetDeviceFullInfoListUseCase @Inject constructor( val roomEncryptionTrustLevel = getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo) val isInactive = checkIfSessionIsInactiveUseCase.execute(deviceInfo.lastSeenTs ?: 0) val isCurrentDevice = currentSessionCrossSigningInfo.deviceId == cryptoDeviceInfo?.deviceId - DeviceFullInfo(deviceInfo, cryptoDeviceInfo, roomEncryptionTrustLevel, isInactive, isCurrentDevice) + val deviceUserAgent = parseDeviceUserAgentUseCase.execute(deviceInfo.lastSeenUserAgent) + DeviceFullInfo(deviceInfo, cryptoDeviceInfo, roomEncryptionTrustLevel, isInactive, isCurrentDevice, deviceUserAgent) } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt index 1ea7dabbe4..fe02aa6400 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt @@ -21,7 +21,9 @@ import javax.inject.Inject class ParseDeviceUserAgentUseCase @Inject constructor() { - fun execute(userAgent: String): DeviceUserAgent { + fun execute(userAgent: String?): DeviceUserAgent { + if (userAgent == null) return createUnknownUserAgent() + return when { userAgent.contains(ANDROID_KEYWORD) -> parseAndroidUserAgent(userAgent) userAgent.contains(IOS_KEYWORD) -> parseIosUserAgent(userAgent) @@ -35,23 +37,26 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { val appName = userAgent.substringBefore("/") val appVersion = userAgent.substringAfter("/").substringBefore(" (") val deviceInfoSegments = userAgent.substringAfter("(").substringBefore(")").split("; ") - val deviceManufacturer = deviceInfoSegments.getOrNull(0) - val deviceModel = deviceInfoSegments.getOrNull(1) - val deviceOsInfo = deviceInfoSegments.getOrNull(2)?.takeIf { it.startsWith("Android") } - val deviceOs = deviceOsInfo?.substringBefore(" ") - val deviceOsVersion = deviceOsInfo?.substringAfter(" ") - return DeviceUserAgent(DeviceType.MOBILE, deviceManufacturer, deviceModel, deviceOs, deviceOsVersion, appName, appVersion) + val deviceModel: String? + val deviceOperatingSystem: String? + if (deviceInfoSegments.firstOrNull() == "Linux") { + val deviceOperatingSystemIndex = deviceInfoSegments.indexOfFirst { it.startsWith("Android") } + deviceOperatingSystem = deviceInfoSegments.getOrNull(deviceOperatingSystemIndex) + deviceModel = deviceInfoSegments.getOrNull(deviceOperatingSystemIndex + 1) + } else { + deviceModel = deviceInfoSegments.getOrNull(0) + deviceOperatingSystem = deviceInfoSegments.getOrNull(1) + } + return DeviceUserAgent(DeviceType.MOBILE, deviceModel, deviceOperatingSystem, appName, appVersion) } private fun parseIosUserAgent(userAgent: String): DeviceUserAgent { val appName = userAgent.substringBefore("/") val appVersion = userAgent.substringAfter("/").substringBefore(" (") val deviceInfoSegments = userAgent.substringAfter("(").substringBefore(")").split("; ") - val deviceManufacturer = "Apple" val deviceModel = deviceInfoSegments.getOrNull(0) - val deviceOs = deviceInfoSegments.getOrNull(1)?.substringBefore(" ") - val deviceOsVersion = deviceInfoSegments.getOrNull(1)?.substringAfter(" ") - return DeviceUserAgent(DeviceType.MOBILE, deviceManufacturer, deviceModel, deviceOs, deviceOsVersion, appName, appVersion) + val deviceOperatingSystem = deviceInfoSegments.getOrNull(1) + return DeviceUserAgent(DeviceType.MOBILE, deviceModel, deviceOperatingSystem, appName, appVersion) } private fun parseDesktopUserAgent(userAgent: String): DeviceUserAgent { @@ -59,9 +64,8 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { val appName = appInfoSegments.getOrNull(0) val appVersion = appInfoSegments.getOrNull(1) val deviceInfoSegments = userAgent.substringAfter("(").substringBefore(")").split("; ") - val deviceOs = deviceInfoSegments.getOrNull(1)?.substringBeforeLast(" ") - val deviceOsVersion = deviceInfoSegments.getOrNull(1)?.substringAfterLast(" ") - return DeviceUserAgent(DeviceType.DESKTOP, null, null, deviceOs, deviceOsVersion, appName, appVersion) + val deviceOperatingSystem = deviceInfoSegments.getOrNull(1) + return DeviceUserAgent(DeviceType.DESKTOP, null, deviceOperatingSystem, appName, appVersion) } private fun parseWebUserAgent(userAgent: String): DeviceUserAgent { @@ -76,6 +80,7 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { companion object { // Element dbg/1.5.0-dev (Xiaomi; Mi 9T; Android 11; RKQ1.200826.002 test-keys; Flavour GooglePlay; MatrixAndroidSdk2 1.5.0) + // Legacy : Element/1.0.0 (Linux; U; Android 6.0.1; SM-A510F Build/MMB29; Flavour GPlay; MatrixAndroidSdk2 1.0) private val ANDROID_KEYWORD = "; MatrixAndroidSdk2" // Element/1.8.21 (iPhone XS Max; iOS 15.2; Scale/3.00) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt index 121973a134..cfaa6aed85 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt @@ -19,12 +19,14 @@ package im.vector.app.features.settings.devices.v2.overview import androidx.lifecycle.asFlow import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.features.settings.devices.v2.DeviceFullInfo +import im.vector.app.features.settings.devices.v2.ParseDeviceUserAgentUseCase import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase import im.vector.app.features.settings.devices.v2.verification.GetCurrentSessionCrossSigningInfoUseCase import im.vector.app.features.settings.devices.v2.verification.GetEncryptionTrustLevelForDeviceUseCase import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emptyFlow +import okhttp3.internal.userAgent import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.flow.unwrap import javax.inject.Inject @@ -34,6 +36,7 @@ class GetDeviceFullInfoUseCase @Inject constructor( private val getCurrentSessionCrossSigningInfoUseCase: GetCurrentSessionCrossSigningInfoUseCase, private val getEncryptionTrustLevelForDeviceUseCase: GetEncryptionTrustLevelForDeviceUseCase, private val checkIfSessionIsInactiveUseCase: CheckIfSessionIsInactiveUseCase, + private val parseDeviceUserAgentUseCase: ParseDeviceUserAgentUseCase, ) { fun execute(deviceId: String): Flow { @@ -49,12 +52,14 @@ class GetDeviceFullInfoUseCase @Inject constructor( val roomEncryptionTrustLevel = getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoInfo) val isInactive = checkIfSessionIsInactiveUseCase.execute(info.lastSeenTs ?: 0) val isCurrentDevice = currentSessionCrossSigningInfo.deviceId == cryptoInfo.deviceId + val deviceUserAgent = parseDeviceUserAgentUseCase.execute(info.lastSeenUserAgent) DeviceFullInfo( deviceInfo = info, cryptoDeviceInfo = cryptoInfo, roomEncryptionTrustLevel = roomEncryptionTrustLevel, isInactive = isInactive, isCurrentDevice = isCurrentDevice, + deviceUserAgent = deviceUserAgent, ) } else { null diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt index 44940dd2c3..e5a3962c6c 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt @@ -19,6 +19,7 @@ package im.vector.app.features.settings.devices.v2 import android.os.SystemClock import com.airbnb.mvrx.Success import com.airbnb.mvrx.test.MvRxTestRule +import im.vector.app.features.settings.devices.v2.list.DeviceType import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCrossSigningInfo import im.vector.app.features.settings.devices.v2.verification.GetCurrentSessionCrossSigningInfoUseCase @@ -36,6 +37,7 @@ import io.mockk.runs import io.mockk.unmockkAll import io.mockk.verify import kotlinx.coroutines.flow.flowOf +import okhttp3.internal.userAgent import org.junit.After import org.junit.Before import org.junit.Rule @@ -242,14 +244,16 @@ class DevicesViewModelTest { cryptoDeviceInfo = verifiedCryptoDeviceInfo, roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, isInactive = false, - isCurrentDevice = true + isCurrentDevice = true, + deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) ) val deviceFullInfo2 = DeviceFullInfo( deviceInfo = mockk(), cryptoDeviceInfo = unverifiedCryptoDeviceInfo, roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning, isInactive = true, - isCurrentDevice = false + isCurrentDevice = false, + deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) ) val deviceFullInfoList = listOf(deviceFullInfo1, deviceFullInfo2) val deviceFullInfoListFlow = flowOf(deviceFullInfoList) diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt index 767819fd24..0537e4f652 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt @@ -19,6 +19,7 @@ package im.vector.app.features.settings.devices.v2 import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType import im.vector.app.features.settings.devices.v2.filter.FilterDevicesUseCase import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase +import im.vector.app.features.settings.devices.v2.list.DeviceType import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCrossSigningInfo import im.vector.app.features.settings.devices.v2.verification.GetCurrentSessionCrossSigningInfoUseCase import im.vector.app.features.settings.devices.v2.verification.GetEncryptionTrustLevelForDeviceUseCase @@ -53,6 +54,7 @@ class GetDeviceFullInfoListUseCaseTest { private val getEncryptionTrustLevelForDeviceUseCase = mockk() private val getCurrentSessionCrossSigningInfoUseCase = mockk() private val filterDevicesUseCase = mockk() + private val parseDeviceUserAgentUseCase = mockk() private val getDeviceFullInfoListUseCase = GetDeviceFullInfoListUseCase( activeSessionHolder = fakeActiveSessionHolder.instance, @@ -60,6 +62,7 @@ class GetDeviceFullInfoListUseCaseTest { getEncryptionTrustLevelForDeviceUseCase = getEncryptionTrustLevelForDeviceUseCase, getCurrentSessionCrossSigningInfoUseCase = getCurrentSessionCrossSigningInfoUseCase, filterDevicesUseCase = filterDevicesUseCase, + parseDeviceUserAgentUseCase = parseDeviceUserAgentUseCase, ) @Before @@ -110,21 +113,24 @@ class GetDeviceFullInfoListUseCaseTest { cryptoDeviceInfo = cryptoDeviceInfo1, roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, isInactive = true, - isCurrentDevice = true + isCurrentDevice = true, + deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) ) val expectedResult2 = DeviceFullInfo( deviceInfo = deviceInfo2, cryptoDeviceInfo = cryptoDeviceInfo2, roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, isInactive = false, - isCurrentDevice = false + isCurrentDevice = false, + deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) ) val expectedResult3 = DeviceFullInfo( deviceInfo = deviceInfo3, cryptoDeviceInfo = cryptoDeviceInfo3, roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning, isInactive = false, - isCurrentDevice = false + isCurrentDevice = false, + deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) ) val expectedResult = listOf(expectedResult3, expectedResult2, expectedResult1) every { filterDevicesUseCase.execute(any(), any()) } returns expectedResult diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt index 2bb5168190..88828419f4 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt @@ -17,6 +17,8 @@ package im.vector.app.features.settings.devices.v2.filter import im.vector.app.features.settings.devices.v2.DeviceFullInfo +import im.vector.app.features.settings.devices.v2.DeviceUserAgent +import im.vector.app.features.settings.devices.v2.list.DeviceType import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldContainAll import org.junit.Test @@ -34,7 +36,8 @@ private val activeVerifiedDevice = DeviceFullInfo( ), roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, isInactive = false, - isCurrentDevice = true + isCurrentDevice = true, + deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) ) private val inactiveVerifiedDevice = DeviceFullInfo( deviceInfo = DeviceInfo(deviceId = "INACTIVE_VERIFIED_DEVICE"), @@ -45,7 +48,8 @@ private val inactiveVerifiedDevice = DeviceFullInfo( ), roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, isInactive = true, - isCurrentDevice = false + isCurrentDevice = false, + deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) ) private val activeUnverifiedDevice = DeviceFullInfo( deviceInfo = DeviceInfo(deviceId = "ACTIVE_UNVERIFIED_DEVICE"), @@ -56,7 +60,8 @@ private val activeUnverifiedDevice = DeviceFullInfo( ), roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning, isInactive = false, - isCurrentDevice = false + isCurrentDevice = false, + deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) ) private val inactiveUnverifiedDevice = DeviceFullInfo( deviceInfo = DeviceInfo(deviceId = "INACTIVE_UNVERIFIED_DEVICE"), @@ -67,7 +72,8 @@ private val inactiveUnverifiedDevice = DeviceFullInfo( ), roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning, isInactive = true, - isCurrentDevice = false + isCurrentDevice = false, + deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) ) private val devices = listOf( diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt index 04cd5fc492..392c737152 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt @@ -19,7 +19,10 @@ package im.vector.app.features.settings.devices.v2.overview import androidx.lifecycle.MutableLiveData import androidx.lifecycle.asFlow import im.vector.app.features.settings.devices.v2.DeviceFullInfo +import im.vector.app.features.settings.devices.v2.DeviceUserAgent +import im.vector.app.features.settings.devices.v2.ParseDeviceUserAgentUseCase import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase +import im.vector.app.features.settings.devices.v2.list.DeviceType import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCrossSigningInfo import im.vector.app.features.settings.devices.v2.verification.GetCurrentSessionCrossSigningInfoUseCase import im.vector.app.features.settings.devices.v2.verification.GetEncryptionTrustLevelForDeviceUseCase @@ -53,12 +56,14 @@ class GetDeviceFullInfoUseCaseTest { private val getEncryptionTrustLevelForDeviceUseCase = mockk() private val checkIfSessionIsInactiveUseCase = mockk() private val fakeFlowLiveDataConversions = FakeFlowLiveDataConversions() + private val parseDeviceUserAgentUseCase = mockk() private val getDeviceFullInfoUseCase = GetDeviceFullInfoUseCase( activeSessionHolder = fakeActiveSessionHolder.instance, getCurrentSessionCrossSigningInfoUseCase = getCurrentSessionCrossSigningInfoUseCase, getEncryptionTrustLevelForDeviceUseCase = getEncryptionTrustLevelForDeviceUseCase, checkIfSessionIsInactiveUseCase = checkIfSessionIsInactiveUseCase, + parseDeviceUserAgentUseCase = parseDeviceUserAgentUseCase, ) @Before @@ -97,7 +102,8 @@ class GetDeviceFullInfoUseCaseTest { cryptoDeviceInfo = cryptoDeviceInfo, roomEncryptionTrustLevel = trustLevel, isInactive = isInactive, - isCurrentDevice = isCurrentDevice + isCurrentDevice = isCurrentDevice, + deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) ) verify { fakeActiveSessionHolder.instance.getSafeActiveSession() } verify { getCurrentSessionCrossSigningInfoUseCase.execute() } From 2bcf0c307d5cc0a0f291e4372df8a5a6256dcea0 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 28 Sep 2022 14:59:56 +0300 Subject: [PATCH 04/16] Add unit test for Android user agents. --- .../v2/ParseDeviceUserAgentUseCaseTest.kt | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt new file mode 100644 index 0000000000..65e80a9b28 --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2 + +import im.vector.app.features.settings.devices.v2.list.DeviceType +import org.amshove.kluent.shouldBeEqualTo +import org.junit.Test + +private val A_USER_AGENT_LIST_FOR_ANDROID = listOf( + // New User Agent Implementation + "Element dbg/1.5.0-dev (Xiaomi Mi 9T; Android 11; RKQ1.200826.002 test-keys; Flavour GooglePlay; MatrixAndroidSdk2 1.5.2)", + "Element/1.5.0 (Samsung SM-G960F; Android 6.0.1; RKQ1.200826.002; Flavour FDroid; MatrixAndroidSdk2 1.5.2)", + "Element/1.5.0 (Google Nexus 5; Android 7.0; RKQ1.200826.002 test test; Flavour FDroid; MatrixAndroidSdk2 1.5.2)", + // Legacy User Agent Implementation + "Element/1.0.0 (Linux; U; Android 6.0.1; SM-A510F Build/MMB29; Flavour GPlay; MatrixAndroidSdk2 1.0)", + "Element/1.0.0 (Linux; Android 7.0; SM-G610M Build/NRD90M; Flavour GPlay; MatrixAndroidSdk2 1.0)", +) +private val AN_EXPECTED_RESULT_LIST_FOR_ANDROID = listOf( + DeviceUserAgent(DeviceType.MOBILE, "Xiaomi Mi 9T", "Android 11", "Element dbg", "1.5.0-dev"), + DeviceUserAgent(DeviceType.MOBILE, "Samsung SM-G960F", "Android 6.0.1", "Element", "1.5.0"), + DeviceUserAgent(DeviceType.MOBILE, "Google Nexus 5", "Android 7.0", "Element", "1.5.0"), + DeviceUserAgent(DeviceType.MOBILE, "SM-A510F Build/MMB29", "Android 6.0.1", "Element", "1.0.0"), + DeviceUserAgent(DeviceType.MOBILE, "SM-G610M Build/NRD90M", "Android 7.0", "Element", "1.0.0"), +) + +class ParseDeviceUserAgentUseCaseTest { + + private val parseDeviceUserAgentUseCase = ParseDeviceUserAgentUseCase() + + @Test + fun `given an Android user agent then it should be parsed as expected`() { + A_USER_AGENT_LIST_FOR_ANDROID.forEachIndexed { index, userAgent -> + parseDeviceUserAgentUseCase.execute(userAgent) shouldBeEqualTo AN_EXPECTED_RESULT_LIST_FOR_ANDROID[index] + } + } +} From 41643ffb538ebea729092bdfae9469d2c81658b5 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 28 Sep 2022 15:00:16 +0300 Subject: [PATCH 05/16] Add unit test for iOS user agents. --- .../v2/ParseDeviceUserAgentUseCaseTest.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt index 65e80a9b28..1e73417ba2 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt @@ -37,6 +37,15 @@ private val AN_EXPECTED_RESULT_LIST_FOR_ANDROID = listOf( DeviceUserAgent(DeviceType.MOBILE, "SM-G610M Build/NRD90M", "Android 7.0", "Element", "1.0.0"), ) +private val A_USER_AGENT_LIST_FOR_IOS = listOf( + "Element/1.8.21 (iPhone; iOS 15.2; Scale/3.00)", + "Element/1.8.21 (iPhone XS Max; iOS 15.2; Scale/3.00)", +) +private val AN_EXPECTED_RESULT_LIST_FOR_IOS = listOf( + DeviceUserAgent(DeviceType.MOBILE, "iPhone", "iOS 15.2", "Element", "1.8.21"), + DeviceUserAgent(DeviceType.MOBILE, "iPhone XS Max", "iOS 15.2", "Element", "1.8.21"), +) + class ParseDeviceUserAgentUseCaseTest { private val parseDeviceUserAgentUseCase = ParseDeviceUserAgentUseCase() @@ -47,4 +56,11 @@ class ParseDeviceUserAgentUseCaseTest { parseDeviceUserAgentUseCase.execute(userAgent) shouldBeEqualTo AN_EXPECTED_RESULT_LIST_FOR_ANDROID[index] } } + + @Test + fun `given an iOS user agent then it should be parsed as expected`() { + A_USER_AGENT_LIST_FOR_IOS.forEachIndexed { index, userAgent -> + parseDeviceUserAgentUseCase.execute(userAgent) shouldBeEqualTo AN_EXPECTED_RESULT_LIST_FOR_IOS[index] + } + } } From 5666383134b9a85d482d8459c73d8dcc9110ec8d Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 28 Sep 2022 18:19:25 +0300 Subject: [PATCH 06/16] Add unit test for desktop and web user agents. --- .../api/session/crypto/model/DeviceInfo.kt | 4 +- .../devices/v2/ParseDeviceUserAgentUseCase.kt | 57 +++++++++++++++++-- .../v2/overview/GetDeviceFullInfoUseCase.kt | 1 - .../devices/v2/DevicesViewModelTest.kt | 1 - .../v2/ParseDeviceUserAgentUseCaseTest.kt | 38 +++++++++++++ 5 files changed, 91 insertions(+), 10 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/DeviceInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/DeviceInfo.kt index 338a594011..500d016002 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/DeviceInfo.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/DeviceInfo.kt @@ -54,10 +54,10 @@ data class DeviceInfo( @Json(name = "last_seen_ip") val lastSeenIp: String? = null, - @Json(name="org.matrix.msc3852.last_seen_user_agent") + @Json(name = "org.matrix.msc3852.last_seen_user_agent") val unstableLastSeenUserAgent: String? = null, - @Json(name="last_seen_user_agent") + @Json(name = "last_seen_user_agent") val lastSeenUserAgent: String? = null, ) : DatedObject { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt index fe02aa6400..eb1daaea0e 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt @@ -17,6 +17,7 @@ package im.vector.app.features.settings.devices.v2 import im.vector.app.features.settings.devices.v2.list.DeviceType +import org.matrix.android.sdk.api.extensions.orFalse import javax.inject.Inject class ParseDeviceUserAgentUseCase @Inject constructor() { @@ -60,12 +61,29 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { } private fun parseDesktopUserAgent(userAgent: String): DeviceUserAgent { - val appInfoSegments = userAgent.substringBeforeLast(" ").substringAfterLast(" ").split("/") - val appName = appInfoSegments.getOrNull(0) - val appVersion = appInfoSegments.getOrNull(1) - val deviceInfoSegments = userAgent.substringAfter("(").substringBefore(")").split("; ") - val deviceOperatingSystem = deviceInfoSegments.getOrNull(1) - return DeviceUserAgent(DeviceType.DESKTOP, null, deviceOperatingSystem, appName, appVersion) + val browserSegments = userAgent.split(" ") + val browserName = when { + isFirefox(browserSegments) -> { + "Firefox" + } + isMobile(browserSegments) -> { + getMobileBrowserName(browserSegments) + } + isSafari(browserSegments) -> { + "Safari" + } + else -> { + getRegularBrowserName(browserSegments) + } + } + + val deviceOperatingSystemSegments = userAgent.substringAfter("(").substringBefore(")").split("; ") + val deviceOperatingSystem = if (deviceOperatingSystemSegments.getOrNull(1)?.startsWith("Android").orFalse()) { + deviceOperatingSystemSegments.getOrNull(1) + } else { + deviceOperatingSystemSegments.getOrNull(0) + } + return DeviceUserAgent(DeviceType.DESKTOP, browserName, deviceOperatingSystem, null, null) } private fun parseWebUserAgent(userAgent: String): DeviceUserAgent { @@ -78,6 +96,33 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { return DeviceUserAgent(DeviceType.UNKNOWN) } + private fun isFirefox(browserSegments: List): Boolean { + return browserSegments.lastOrNull()?.startsWith("Firefox").orFalse() + } + + private fun isSafari(browserSegments: List): Boolean { + return browserSegments.lastOrNull()?.startsWith("Safari").orFalse() && + browserSegments.getOrNull(browserSegments.size - 2)?.startsWith("Version").orFalse() + } + + private fun isMobile(browserSegments: List): Boolean { + return browserSegments.lastOrNull()?.startsWith("Safari").orFalse() && + browserSegments.getOrNull(browserSegments.size - 2) == "Mobile" + } + + private fun getMobileBrowserName(browserSegments: List): String? { + val possibleBrowserName = browserSegments.getOrNull(browserSegments.size - 3)?.split("/")?.firstOrNull() + return if (possibleBrowserName == "Version") { + "Safari" + } else { + possibleBrowserName + } + } + + private fun getRegularBrowserName(browserSegments: List): String? { + return browserSegments.getOrNull(browserSegments.size - 2)?.split("/")?.firstOrNull() + } + companion object { // Element dbg/1.5.0-dev (Xiaomi; Mi 9T; Android 11; RKQ1.200826.002 test-keys; Flavour GooglePlay; MatrixAndroidSdk2 1.5.0) // Legacy : Element/1.0.0 (Linux; U; Android 6.0.1; SM-A510F Build/MMB29; Flavour GPlay; MatrixAndroidSdk2 1.0) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt index cfaa6aed85..fd612308fc 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt @@ -26,7 +26,6 @@ import im.vector.app.features.settings.devices.v2.verification.GetEncryptionTrus import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emptyFlow -import okhttp3.internal.userAgent import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.flow.unwrap import javax.inject.Inject diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt index e5a3962c6c..3b011ae9bb 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt @@ -37,7 +37,6 @@ import io.mockk.runs import io.mockk.unmockkAll import io.mockk.verify import kotlinx.coroutines.flow.flowOf -import okhttp3.internal.userAgent import org.junit.After import org.junit.Before import org.junit.Rule diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt index 1e73417ba2..8eea1f7136 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt @@ -46,6 +46,30 @@ private val AN_EXPECTED_RESULT_LIST_FOR_IOS = listOf( DeviceUserAgent(DeviceType.MOBILE, "iPhone XS Max", "iOS 15.2", "Element", "1.8.21"), ) +private val A_USER_AGENT_LIST_FOR_DESKTOP = listOf( + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) ElementNightly/2022091301 Chrome/104.0.5112.102 Electron/20.1.1 Safari/537.36", + "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( + DeviceUserAgent(DeviceType.DESKTOP, "Electron", "Macintosh", null, null), + DeviceUserAgent(DeviceType.DESKTOP, "Electron", "Windows NT 10.0", null, null), +) + +private val A_USER_AGENT_LIST_FOR_WEB = listOf( + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36", + "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:39.0) Gecko/20100101 Firefox/39.0", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.3.18 (KHTML, like Gecko) Version/8.0.3 Safari/600.3.18", + "Mozilla/5.0 (Linux; Android 9; SM-G973U Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36", + ) +private val AN_EXPECTED_RESULT_LIST_FOR_WEB = listOf( + DeviceUserAgent(DeviceType.WEB, "Chrome", "Macintosh", null, null), + DeviceUserAgent(DeviceType.WEB, "Chrome", "Windows NT 10.0", null, null), + DeviceUserAgent(DeviceType.WEB, "Firefox", "Macintosh", null, null), + DeviceUserAgent(DeviceType.WEB, "Safari", "Macintosh", null, null), + DeviceUserAgent(DeviceType.WEB, "Chrome", "Android 9", null, null), +) + class ParseDeviceUserAgentUseCaseTest { private val parseDeviceUserAgentUseCase = ParseDeviceUserAgentUseCase() @@ -63,4 +87,18 @@ class ParseDeviceUserAgentUseCaseTest { parseDeviceUserAgentUseCase.execute(userAgent) shouldBeEqualTo AN_EXPECTED_RESULT_LIST_FOR_IOS[index] } } + + @Test + fun `given a Desktop user agent then it should be parsed as expected`() { + A_USER_AGENT_LIST_FOR_DESKTOP.forEachIndexed { index, userAgent -> + parseDeviceUserAgentUseCase.execute(userAgent) shouldBeEqualTo AN_EXPECTED_RESULT_LIST_FOR_DESKTOP[index] + } + } + + @Test + fun `given a Web user agent then it should be parsed as expected`() { + A_USER_AGENT_LIST_FOR_WEB.forEachIndexed { index, userAgent -> + parseDeviceUserAgentUseCase.execute(userAgent) shouldBeEqualTo AN_EXPECTED_RESULT_LIST_FOR_WEB[index] + } + } } From 7a36b10d4048f0afe6160dbd01c698a6a137c05f Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 28 Sep 2022 18:22:24 +0300 Subject: [PATCH 07/16] Add changelog. --- changelog.d/7247.wip | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7247.wip diff --git a/changelog.d/7247.wip b/changelog.d/7247.wip new file mode 100644 index 0000000000..8f2a447742 --- /dev/null +++ b/changelog.d/7247.wip @@ -0,0 +1 @@ +[Device Manager] Parse user agents From c16b5d674c9b2fb705d0f38f21d6d67512d5cbb8 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 29 Sep 2022 11:36:38 +0300 Subject: [PATCH 08/16] Add more user agent support. --- .../devices/v2/ParseDeviceUserAgentUseCase.kt | 13 ++++++++++--- .../v2/ParseDeviceUserAgentUseCaseTest.kt | 16 ++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt index eb1daaea0e..f6d12d7b4b 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt @@ -37,7 +37,7 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { private fun parseAndroidUserAgent(userAgent: String): DeviceUserAgent { val appName = userAgent.substringBefore("/") val appVersion = userAgent.substringAfter("/").substringBefore(" (") - val deviceInfoSegments = userAgent.substringAfter("(").substringBefore(")").split("; ") + val deviceInfoSegments = userAgent.substringAfter("(").substringBeforeLast(")").split("; ") val deviceModel: String? val deviceOperatingSystem: String? if (deviceInfoSegments.firstOrNull() == "Linux") { @@ -54,7 +54,7 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { private fun parseIosUserAgent(userAgent: String): DeviceUserAgent { val appName = userAgent.substringBefore("/") val appVersion = userAgent.substringAfter("/").substringBefore(" (") - val deviceInfoSegments = userAgent.substringAfter("(").substringBefore(")").split("; ") + val deviceInfoSegments = userAgent.substringAfter("(").substringBeforeLast(")").split("; ") val deviceModel = deviceInfoSegments.getOrNull(0) val deviceOperatingSystem = deviceInfoSegments.getOrNull(1) return DeviceUserAgent(DeviceType.MOBILE, deviceModel, deviceOperatingSystem, appName, appVersion) @@ -66,6 +66,9 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { isFirefox(browserSegments) -> { "Firefox" } + isEdge(browserSegments) -> { + "Edge" + } isMobile(browserSegments) -> { getMobileBrowserName(browserSegments) } @@ -100,6 +103,10 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { return browserSegments.lastOrNull()?.startsWith("Firefox").orFalse() } + private fun isEdge(browserSegments: List): Boolean { + return browserSegments.lastOrNull()?.startsWith("Edge").orFalse() + } + private fun isSafari(browserSegments: List): Boolean { return browserSegments.lastOrNull()?.startsWith("Safari").orFalse() && browserSegments.getOrNull(browserSegments.size - 2)?.startsWith("Version").orFalse() @@ -107,7 +114,7 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { private fun isMobile(browserSegments: List): Boolean { return browserSegments.lastOrNull()?.startsWith("Safari").orFalse() && - browserSegments.getOrNull(browserSegments.size - 2) == "Mobile" + browserSegments.getOrNull(browserSegments.size - 2)?.startsWith("Mobile").orFalse() } private fun getMobileBrowserName(browserSegments: List): String? { diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt index 8eea1f7136..afd3000af2 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt @@ -25,6 +25,8 @@ private val A_USER_AGENT_LIST_FOR_ANDROID = listOf( "Element dbg/1.5.0-dev (Xiaomi Mi 9T; Android 11; RKQ1.200826.002 test-keys; Flavour GooglePlay; MatrixAndroidSdk2 1.5.2)", "Element/1.5.0 (Samsung SM-G960F; Android 6.0.1; RKQ1.200826.002; Flavour FDroid; MatrixAndroidSdk2 1.5.2)", "Element/1.5.0 (Google Nexus 5; Android 7.0; RKQ1.200826.002 test test; Flavour FDroid; MatrixAndroidSdk2 1.5.2)", + "Element/1.5.0 (Google (Nexus) 5; Android 7.0; RKQ1.200826.002 test test; Flavour FDroid; MatrixAndroidSdk2 1.5.2)", + "Element/1.5.0 (Google (Nexus) (5); Android 7.0; RKQ1.200826.002 test test; Flavour FDroid; MatrixAndroidSdk2 1.5.2)", // Legacy User Agent Implementation "Element/1.0.0 (Linux; U; Android 6.0.1; SM-A510F Build/MMB29; Flavour GPlay; MatrixAndroidSdk2 1.0)", "Element/1.0.0 (Linux; Android 7.0; SM-G610M Build/NRD90M; Flavour GPlay; MatrixAndroidSdk2 1.0)", @@ -33,6 +35,8 @@ private val AN_EXPECTED_RESULT_LIST_FOR_ANDROID = listOf( DeviceUserAgent(DeviceType.MOBILE, "Xiaomi Mi 9T", "Android 11", "Element dbg", "1.5.0-dev"), DeviceUserAgent(DeviceType.MOBILE, "Samsung SM-G960F", "Android 6.0.1", "Element", "1.5.0"), DeviceUserAgent(DeviceType.MOBILE, "Google Nexus 5", "Android 7.0", "Element", "1.5.0"), + DeviceUserAgent(DeviceType.MOBILE, "Google (Nexus) 5", "Android 7.0", "Element", "1.5.0"), + DeviceUserAgent(DeviceType.MOBILE, "Google (Nexus) (5)", "Android 7.0", "Element", "1.5.0"), DeviceUserAgent(DeviceType.MOBILE, "SM-A510F Build/MMB29", "Android 6.0.1", "Element", "1.0.0"), DeviceUserAgent(DeviceType.MOBILE, "SM-G610M Build/NRD90M", "Android 7.0", "Element", "1.0.0"), ) @@ -40,10 +44,14 @@ private val AN_EXPECTED_RESULT_LIST_FOR_ANDROID = listOf( private val A_USER_AGENT_LIST_FOR_IOS = listOf( "Element/1.8.21 (iPhone; iOS 15.2; Scale/3.00)", "Element/1.8.21 (iPhone XS Max; iOS 15.2; Scale/3.00)", + "Element/1.8.21 (iPad Pro (11-inch); iOS 15.2; Scale/3.00)", + "Element/1.8.21 (iPad Pro (12.9-inch) (3rd generation); iOS 15.2; Scale/3.00)", ) private val AN_EXPECTED_RESULT_LIST_FOR_IOS = listOf( DeviceUserAgent(DeviceType.MOBILE, "iPhone", "iOS 15.2", "Element", "1.8.21"), DeviceUserAgent(DeviceType.MOBILE, "iPhone XS Max", "iOS 15.2", "Element", "1.8.21"), + DeviceUserAgent(DeviceType.MOBILE, "iPad Pro (11-inch)", "iOS 15.2", "Element", "1.8.21"), + DeviceUserAgent(DeviceType.MOBILE, "iPad Pro (12.9-inch) (3rd generation)", "iOS 15.2", "Element", "1.8.21"), ) private val A_USER_AGENT_LIST_FOR_DESKTOP = listOf( @@ -61,6 +69,10 @@ private val A_USER_AGENT_LIST_FOR_WEB = listOf( "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:39.0) Gecko/20100101 Firefox/39.0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.3.18 (KHTML, like Gecko) Version/8.0.3 Safari/600.3.18", "Mozilla/5.0 (Linux; Android 9; SM-G973U Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36", + "Mozilla/5.0 (iPad; CPU OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12H321 Safari/600.1.4", + "Mozilla/5.0 (iPhone; CPU iPhone OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12H321 Safari/600.1.4", + "Mozilla/5.0 (Windows NT 6.0; rv:40.0) Gecko/20100101 Firefox/40.0", + "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( DeviceUserAgent(DeviceType.WEB, "Chrome", "Macintosh", null, null), @@ -68,6 +80,10 @@ private val AN_EXPECTED_RESULT_LIST_FOR_WEB = listOf( DeviceUserAgent(DeviceType.WEB, "Firefox", "Macintosh", null, null), DeviceUserAgent(DeviceType.WEB, "Safari", "Macintosh", null, null), DeviceUserAgent(DeviceType.WEB, "Chrome", "Android 9", null, null), + DeviceUserAgent(DeviceType.WEB, "Safari", "iPad", null, null), + DeviceUserAgent(DeviceType.WEB, "Safari", "iPhone", null, null), + DeviceUserAgent(DeviceType.WEB, "Firefox", "Windows NT 6.0", null, null), + DeviceUserAgent(DeviceType.WEB, "Edge", "Windows NT 10.0", null, null), ) class ParseDeviceUserAgentUseCaseTest { From 4c173a79aeacfc82dfff215dd80f709aeae78cb4 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 29 Sep 2022 11:40:15 +0300 Subject: [PATCH 09/16] Lint fixes. --- .../features/settings/devices/v2/DeviceUserAgent.kt | 12 ++++++------ .../devices/v2/ParseDeviceUserAgentUseCase.kt | 11 ++++++----- .../devices/v2/ParseDeviceUserAgentUseCaseTest.kt | 6 ++++-- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt index cf201fafa4..28fa8af41c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt @@ -20,27 +20,27 @@ import im.vector.app.features.settings.devices.v2.list.DeviceType data class DeviceUserAgent( /** - * One of MOBILE, WEB, DESKTOP or UNKNOWN + * One of MOBILE, WEB, DESKTOP or UNKNOWN. */ val deviceType: DeviceType, /** - * i.e. Google Pixel 6 + * i.e. Google Pixel 6. */ val deviceModel: String? = null, /** - * i.e. Android 11 + * i.e. Android 11. */ val deviceOperatingSystem: String? = null, /** - * i.e. Element Nightly + * i.e. Element Nightly. */ val clientName: String? = null, /** - * i.e. 1.5.0 + * i.e. 1.5.0. */ val clientVersion: String? = null, /** - * i.e. Chrome + * i.e. Chrome. */ val browser: String? = null, ) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt index f6d12d7b4b..48ea1b2ad0 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt @@ -133,15 +133,16 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { companion object { // Element dbg/1.5.0-dev (Xiaomi; Mi 9T; Android 11; RKQ1.200826.002 test-keys; Flavour GooglePlay; MatrixAndroidSdk2 1.5.0) // Legacy : Element/1.0.0 (Linux; U; Android 6.0.1; SM-A510F Build/MMB29; Flavour GPlay; MatrixAndroidSdk2 1.0) - private val ANDROID_KEYWORD = "; MatrixAndroidSdk2" + private const val ANDROID_KEYWORD = "; MatrixAndroidSdk2" // Element/1.8.21 (iPhone XS Max; iOS 15.2; Scale/3.00) - private val IOS_KEYWORD = "; iOS " + private const val IOS_KEYWORD = "; iOS " - // Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) ElementNightly/2022091301 Chrome/104.0.5112.102 Electron/20.1.1 Safari/537.36 - private val DESKTOP_KEYWORD = " Electron/" + // Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) ElementNightly/2022091301 + // Chrome/104.0.5112.102 Electron/20.1.1 Safari/537.36 + private const val DESKTOP_KEYWORD = " Electron/" // 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 val WEB_KEYWORD = "Mozilla/" + private const val WEB_KEYWORD = "Mozilla/" } } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt index afd3000af2..0cc59f1d14 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt @@ -51,11 +51,13 @@ private val AN_EXPECTED_RESULT_LIST_FOR_IOS = listOf( DeviceUserAgent(DeviceType.MOBILE, "iPhone", "iOS 15.2", "Element", "1.8.21"), DeviceUserAgent(DeviceType.MOBILE, "iPhone XS Max", "iOS 15.2", "Element", "1.8.21"), DeviceUserAgent(DeviceType.MOBILE, "iPad Pro (11-inch)", "iOS 15.2", "Element", "1.8.21"), - DeviceUserAgent(DeviceType.MOBILE, "iPad Pro (12.9-inch) (3rd generation)", "iOS 15.2", "Element", "1.8.21"), + DeviceUserAgent(DeviceType.MOBILE, "iPad Pro (12.9-inch) (3rd generation)", "iOS 15.2", + "Element", "1.8.21"), ) private val A_USER_AGENT_LIST_FOR_DESKTOP = listOf( - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) ElementNightly/2022091301 Chrome/104.0.5112.102 Electron/20.1.1 Safari/537.36", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) ElementNightly/2022091301 Chrome/104.0.5112.102" + + " Electron/20.1.1 Safari/537.36", "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( From 8663fe8c74f8796b4a9645b79fa2262588554705 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 29 Sep 2022 12:35:15 +0300 Subject: [PATCH 10/16] Add tests for unknown user agents. --- .../v2/ParseDeviceUserAgentUseCaseTest.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt index 0cc59f1d14..4797a079a2 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt @@ -88,6 +88,15 @@ private val AN_EXPECTED_RESULT_LIST_FOR_WEB = listOf( DeviceUserAgent(DeviceType.WEB, "Edge", "Windows NT 10.0", null, null), ) +private val AN_UNKNOWN_USER_AGENT_LIST = listOf( + "AppleTV11,1/11.1", + "Curl Client/1.0", +) +private val AN_UNKNOWN_USER_AGENT_EXPECTED_RESULT_LIST = listOf( + DeviceUserAgent(DeviceType.UNKNOWN, null, null, null, null), + DeviceUserAgent(DeviceType.UNKNOWN, null, null, null, null), +) + class ParseDeviceUserAgentUseCaseTest { private val parseDeviceUserAgentUseCase = ParseDeviceUserAgentUseCase() @@ -119,4 +128,11 @@ class ParseDeviceUserAgentUseCaseTest { parseDeviceUserAgentUseCase.execute(userAgent) shouldBeEqualTo AN_EXPECTED_RESULT_LIST_FOR_WEB[index] } } + + @Test + fun `given an unknown user agent then it should be parsed as expected`() { + AN_UNKNOWN_USER_AGENT_LIST.forEachIndexed { index, userAgent -> + parseDeviceUserAgentUseCase.execute(userAgent) shouldBeEqualTo AN_UNKNOWN_USER_AGENT_EXPECTED_RESULT_LIST[index] + } + } } From 38cd2be33223a1dc9af5be9b33cd4a616df9e9f2 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 29 Sep 2022 13:28:46 +0300 Subject: [PATCH 11/16] Fix broken tests. --- .../v2/GetDeviceFullInfoListUseCase.kt | 2 +- .../v2/overview/GetDeviceFullInfoUseCase.kt | 2 +- .../v2/GetDeviceFullInfoListUseCaseTest.kt | 19 ++++++++++++++++--- .../overview/GetDeviceFullInfoUseCaseTest.kt | 3 ++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt index e4cdbb0a87..0272bea351 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt @@ -73,7 +73,7 @@ class GetDeviceFullInfoListUseCase @Inject constructor( val roomEncryptionTrustLevel = getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo) val isInactive = checkIfSessionIsInactiveUseCase.execute(deviceInfo.lastSeenTs ?: 0) val isCurrentDevice = currentSessionCrossSigningInfo.deviceId == cryptoDeviceInfo?.deviceId - val deviceUserAgent = parseDeviceUserAgentUseCase.execute(deviceInfo.lastSeenUserAgent) + val deviceUserAgent = parseDeviceUserAgentUseCase.execute(deviceInfo.getBestLastSeenUserAgent()) DeviceFullInfo(deviceInfo, cryptoDeviceInfo, roomEncryptionTrustLevel, isInactive, isCurrentDevice, deviceUserAgent) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt index fd612308fc..9ed77d3834 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt @@ -51,7 +51,7 @@ class GetDeviceFullInfoUseCase @Inject constructor( val roomEncryptionTrustLevel = getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoInfo) val isInactive = checkIfSessionIsInactiveUseCase.execute(info.lastSeenTs ?: 0) val isCurrentDevice = currentSessionCrossSigningInfo.deviceId == cryptoInfo.deviceId - val deviceUserAgent = parseDeviceUserAgentUseCase.execute(info.lastSeenUserAgent) + val deviceUserAgent = parseDeviceUserAgentUseCase.execute(info.getBestLastSeenUserAgent()) DeviceFullInfo( deviceInfo = info, cryptoDeviceInfo = cryptoInfo, diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt index 0537e4f652..3422a01694 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt @@ -46,6 +46,7 @@ private const val A_DEVICE_ID_3 = "device-id-3" private const val A_TIMESTAMP_1 = 100L private const val A_TIMESTAMP_2 = 200L private const val A_TIMESTAMP_3 = 300L +private const val A_USER_AGENT = "Element dbg/1.5.0-dev (Xiaomi Mi 9T; Android 11; RKQ1.200826.002 test-keys; Flavour GooglePlay; MatrixAndroidSdk2 1.5.2)" class GetDeviceFullInfoListUseCaseTest { @@ -90,21 +91,24 @@ class GetDeviceFullInfoListUseCaseTest { lastSeenTs = A_TIMESTAMP_1, isInactive = true, roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, - cryptoDeviceInfo = cryptoDeviceInfo1 + cryptoDeviceInfo = cryptoDeviceInfo1, + lastSeenUserAgent = A_USER_AGENT ) val deviceInfo2 = givenADevicesInfo( deviceId = A_DEVICE_ID_2, lastSeenTs = A_TIMESTAMP_2, isInactive = false, roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, - cryptoDeviceInfo = cryptoDeviceInfo2 + cryptoDeviceInfo = cryptoDeviceInfo2, + lastSeenUserAgent = A_USER_AGENT ) val deviceInfo3 = givenADevicesInfo( deviceId = A_DEVICE_ID_3, lastSeenTs = A_TIMESTAMP_3, isInactive = false, roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning, - cryptoDeviceInfo = cryptoDeviceInfo3 + cryptoDeviceInfo = cryptoDeviceInfo3, + lastSeenUserAgent = A_USER_AGENT ) val deviceInfoList = listOf(deviceInfo1, deviceInfo2, deviceInfo3) every { fakeFlowSession.liveMyDevicesInfo() } returns flowOf(deviceInfoList) @@ -188,12 +192,21 @@ class GetDeviceFullInfoListUseCaseTest { isInactive: Boolean, roomEncryptionTrustLevel: RoomEncryptionTrustLevel, cryptoDeviceInfo: CryptoDeviceInfo, + lastSeenUserAgent: String, ): DeviceInfo { val deviceInfo = mockk() every { deviceInfo.deviceId } returns deviceId every { deviceInfo.lastSeenTs } returns lastSeenTs + every { deviceInfo.getBestLastSeenUserAgent() } returns lastSeenUserAgent every { getEncryptionTrustLevelForDeviceUseCase.execute(any(), cryptoDeviceInfo) } returns roomEncryptionTrustLevel every { checkIfSessionIsInactiveUseCase.execute(lastSeenTs) } returns isInactive + every { parseDeviceUserAgentUseCase.execute(lastSeenUserAgent) } returns DeviceUserAgent( + DeviceType.MOBILE, + "Xiaomi Mi 9T", + "Android 11", + "Element dbg", + "1.5.0-dev" + ) return deviceInfo } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt index 392c737152..425935d065 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt @@ -81,7 +81,7 @@ class GetDeviceFullInfoUseCaseTest { // Given val currentSessionCrossSigningInfo = givenCurrentSessionCrossSigningInfo() val deviceInfo = DeviceInfo( - lastSeenTs = A_TIMESTAMP + lastSeenTs = A_TIMESTAMP, ) fakeActiveSessionHolder.fakeSession.fakeCryptoService.myDevicesInfoWithIdLiveData = MutableLiveData(Optional(deviceInfo)) fakeActiveSessionHolder.fakeSession.fakeCryptoService.myDevicesInfoWithIdLiveData.givenAsFlow() @@ -92,6 +92,7 @@ class GetDeviceFullInfoUseCaseTest { val isInactive = false val isCurrentDevice = true every { checkIfSessionIsInactiveUseCase.execute(any()) } returns isInactive + every { parseDeviceUserAgentUseCase.execute(any()) } returns DeviceUserAgent(DeviceType.MOBILE) // When val deviceFullInfo = getDeviceFullInfoUseCase.execute(A_DEVICE_ID).firstOrNull() From 6d459a0bc95c2b769806525a68327358e19136d2 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 29 Sep 2022 17:35:47 +0300 Subject: [PATCH 12/16] Code review fixes. --- ...viceUserAgent.kt => DeviceExtendedInfo.kt} | 2 +- .../settings/devices/v2/DeviceFullInfo.kt | 2 +- .../devices/v2/ParseDeviceUserAgentUseCase.kt | 20 ++++---- .../v2/overview/GetDeviceFullInfoUseCase.kt | 2 +- .../devices/v2/DevicesViewModelTest.kt | 4 +- .../v2/GetDeviceFullInfoListUseCaseTest.kt | 19 ++------ .../v2/ParseDeviceUserAgentUseCaseTest.kt | 48 +++++++++---------- .../v2/filter/FilterDevicesUseCaseTest.kt | 10 ++-- .../overview/GetDeviceFullInfoUseCaseTest.kt | 6 +-- 9 files changed, 52 insertions(+), 61 deletions(-) rename vector/src/main/java/im/vector/app/features/settings/devices/v2/{DeviceUserAgent.kt => DeviceExtendedInfo.kt} (97%) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceExtendedInfo.kt similarity index 97% rename from vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt rename to vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceExtendedInfo.kt index 28fa8af41c..c9d27d093b 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceUserAgent.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceExtendedInfo.kt @@ -18,7 +18,7 @@ package im.vector.app.features.settings.devices.v2 import im.vector.app.features.settings.devices.v2.list.DeviceType -data class DeviceUserAgent( +data class DeviceExtendedInfo( /** * One of MOBILE, WEB, DESKTOP or UNKNOWN. */ diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceFullInfo.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceFullInfo.kt index a47a9340c3..445eb6226f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceFullInfo.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceFullInfo.kt @@ -26,5 +26,5 @@ data class DeviceFullInfo( val roomEncryptionTrustLevel: RoomEncryptionTrustLevel, val isInactive: Boolean, val isCurrentDevice: Boolean, - val deviceUserAgent: DeviceUserAgent, + val deviceExtendedInfo: DeviceExtendedInfo, ) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt index 48ea1b2ad0..a4fe5f4096 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt @@ -22,7 +22,7 @@ import javax.inject.Inject class ParseDeviceUserAgentUseCase @Inject constructor() { - fun execute(userAgent: String?): DeviceUserAgent { + fun execute(userAgent: String?): DeviceExtendedInfo { if (userAgent == null) return createUnknownUserAgent() return when { @@ -34,7 +34,7 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { } } - private fun parseAndroidUserAgent(userAgent: String): DeviceUserAgent { + private fun parseAndroidUserAgent(userAgent: String): DeviceExtendedInfo { val appName = userAgent.substringBefore("/") val appVersion = userAgent.substringAfter("/").substringBefore(" (") val deviceInfoSegments = userAgent.substringAfter("(").substringBeforeLast(")").split("; ") @@ -48,19 +48,19 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { deviceModel = deviceInfoSegments.getOrNull(0) deviceOperatingSystem = deviceInfoSegments.getOrNull(1) } - return DeviceUserAgent(DeviceType.MOBILE, deviceModel, deviceOperatingSystem, appName, appVersion) + return DeviceExtendedInfo(DeviceType.MOBILE, deviceModel, deviceOperatingSystem, appName, appVersion) } - private fun parseIosUserAgent(userAgent: String): DeviceUserAgent { + private fun parseIosUserAgent(userAgent: String): DeviceExtendedInfo { val appName = userAgent.substringBefore("/") val appVersion = userAgent.substringAfter("/").substringBefore(" (") val deviceInfoSegments = userAgent.substringAfter("(").substringBeforeLast(")").split("; ") val deviceModel = deviceInfoSegments.getOrNull(0) val deviceOperatingSystem = deviceInfoSegments.getOrNull(1) - return DeviceUserAgent(DeviceType.MOBILE, deviceModel, deviceOperatingSystem, appName, appVersion) + return DeviceExtendedInfo(DeviceType.MOBILE, deviceModel, deviceOperatingSystem, appName, appVersion) } - private fun parseDesktopUserAgent(userAgent: String): DeviceUserAgent { + private fun parseDesktopUserAgent(userAgent: String): DeviceExtendedInfo { val browserSegments = userAgent.split(" ") val browserName = when { isFirefox(browserSegments) -> { @@ -86,17 +86,17 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { } else { deviceOperatingSystemSegments.getOrNull(0) } - return DeviceUserAgent(DeviceType.DESKTOP, browserName, deviceOperatingSystem, null, null) + return DeviceExtendedInfo(DeviceType.DESKTOP, browserName, deviceOperatingSystem, null, null) } - private fun parseWebUserAgent(userAgent: String): DeviceUserAgent { + private fun parseWebUserAgent(userAgent: String): DeviceExtendedInfo { return parseDesktopUserAgent(userAgent).copy( deviceType = DeviceType.WEB ) } - private fun createUnknownUserAgent(): DeviceUserAgent { - return DeviceUserAgent(DeviceType.UNKNOWN) + private fun createUnknownUserAgent(): DeviceExtendedInfo { + return DeviceExtendedInfo(DeviceType.UNKNOWN) } private fun isFirefox(browserSegments: List): Boolean { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt index 9ed77d3834..42cd49b072 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt @@ -58,7 +58,7 @@ class GetDeviceFullInfoUseCase @Inject constructor( roomEncryptionTrustLevel = roomEncryptionTrustLevel, isInactive = isInactive, isCurrentDevice = isCurrentDevice, - deviceUserAgent = deviceUserAgent, + deviceExtendedInfo = deviceUserAgent, ) } else { null diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt index 3b011ae9bb..82d269034e 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt @@ -244,7 +244,7 @@ class DevicesViewModelTest { roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, isInactive = false, isCurrentDevice = true, - deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) + deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE) ) val deviceFullInfo2 = DeviceFullInfo( deviceInfo = mockk(), @@ -252,7 +252,7 @@ class DevicesViewModelTest { roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning, isInactive = true, isCurrentDevice = false, - deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) + deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE) ) val deviceFullInfoList = listOf(deviceFullInfo1, deviceFullInfo2) val deviceFullInfoListFlow = flowOf(deviceFullInfoList) diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt index 3422a01694..efeb7f91b8 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt @@ -46,7 +46,6 @@ private const val A_DEVICE_ID_3 = "device-id-3" private const val A_TIMESTAMP_1 = 100L private const val A_TIMESTAMP_2 = 200L private const val A_TIMESTAMP_3 = 300L -private const val A_USER_AGENT = "Element dbg/1.5.0-dev (Xiaomi Mi 9T; Android 11; RKQ1.200826.002 test-keys; Flavour GooglePlay; MatrixAndroidSdk2 1.5.2)" class GetDeviceFullInfoListUseCaseTest { @@ -92,7 +91,6 @@ class GetDeviceFullInfoListUseCaseTest { isInactive = true, roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, cryptoDeviceInfo = cryptoDeviceInfo1, - lastSeenUserAgent = A_USER_AGENT ) val deviceInfo2 = givenADevicesInfo( deviceId = A_DEVICE_ID_2, @@ -100,7 +98,6 @@ class GetDeviceFullInfoListUseCaseTest { isInactive = false, roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, cryptoDeviceInfo = cryptoDeviceInfo2, - lastSeenUserAgent = A_USER_AGENT ) val deviceInfo3 = givenADevicesInfo( deviceId = A_DEVICE_ID_3, @@ -108,7 +105,6 @@ class GetDeviceFullInfoListUseCaseTest { isInactive = false, roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning, cryptoDeviceInfo = cryptoDeviceInfo3, - lastSeenUserAgent = A_USER_AGENT ) val deviceInfoList = listOf(deviceInfo1, deviceInfo2, deviceInfo3) every { fakeFlowSession.liveMyDevicesInfo() } returns flowOf(deviceInfoList) @@ -118,7 +114,7 @@ class GetDeviceFullInfoListUseCaseTest { roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, isInactive = true, isCurrentDevice = true, - deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) + deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE) ) val expectedResult2 = DeviceFullInfo( deviceInfo = deviceInfo2, @@ -126,7 +122,7 @@ class GetDeviceFullInfoListUseCaseTest { roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, isInactive = false, isCurrentDevice = false, - deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) + deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE) ) val expectedResult3 = DeviceFullInfo( deviceInfo = deviceInfo3, @@ -134,7 +130,7 @@ class GetDeviceFullInfoListUseCaseTest { roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning, isInactive = false, isCurrentDevice = false, - deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) + deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE) ) val expectedResult = listOf(expectedResult3, expectedResult2, expectedResult1) every { filterDevicesUseCase.execute(any(), any()) } returns expectedResult @@ -192,20 +188,15 @@ class GetDeviceFullInfoListUseCaseTest { isInactive: Boolean, roomEncryptionTrustLevel: RoomEncryptionTrustLevel, cryptoDeviceInfo: CryptoDeviceInfo, - lastSeenUserAgent: String, ): DeviceInfo { val deviceInfo = mockk() every { deviceInfo.deviceId } returns deviceId every { deviceInfo.lastSeenTs } returns lastSeenTs - every { deviceInfo.getBestLastSeenUserAgent() } returns lastSeenUserAgent + every { deviceInfo.getBestLastSeenUserAgent() } returns "" every { getEncryptionTrustLevelForDeviceUseCase.execute(any(), cryptoDeviceInfo) } returns roomEncryptionTrustLevel every { checkIfSessionIsInactiveUseCase.execute(lastSeenTs) } returns isInactive - every { parseDeviceUserAgentUseCase.execute(lastSeenUserAgent) } returns DeviceUserAgent( + every { parseDeviceUserAgentUseCase.execute(any()) } returns DeviceExtendedInfo( DeviceType.MOBILE, - "Xiaomi Mi 9T", - "Android 11", - "Element dbg", - "1.5.0-dev" ) return deviceInfo diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt index 4797a079a2..0cb0019a2a 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt @@ -32,13 +32,13 @@ private val A_USER_AGENT_LIST_FOR_ANDROID = listOf( "Element/1.0.0 (Linux; Android 7.0; SM-G610M Build/NRD90M; Flavour GPlay; MatrixAndroidSdk2 1.0)", ) private val AN_EXPECTED_RESULT_LIST_FOR_ANDROID = listOf( - DeviceUserAgent(DeviceType.MOBILE, "Xiaomi Mi 9T", "Android 11", "Element dbg", "1.5.0-dev"), - DeviceUserAgent(DeviceType.MOBILE, "Samsung SM-G960F", "Android 6.0.1", "Element", "1.5.0"), - DeviceUserAgent(DeviceType.MOBILE, "Google Nexus 5", "Android 7.0", "Element", "1.5.0"), - DeviceUserAgent(DeviceType.MOBILE, "Google (Nexus) 5", "Android 7.0", "Element", "1.5.0"), - DeviceUserAgent(DeviceType.MOBILE, "Google (Nexus) (5)", "Android 7.0", "Element", "1.5.0"), - DeviceUserAgent(DeviceType.MOBILE, "SM-A510F Build/MMB29", "Android 6.0.1", "Element", "1.0.0"), - DeviceUserAgent(DeviceType.MOBILE, "SM-G610M Build/NRD90M", "Android 7.0", "Element", "1.0.0"), + DeviceExtendedInfo(DeviceType.MOBILE, "Xiaomi Mi 9T", "Android 11", "Element dbg", "1.5.0-dev"), + DeviceExtendedInfo(DeviceType.MOBILE, "Samsung SM-G960F", "Android 6.0.1", "Element", "1.5.0"), + DeviceExtendedInfo(DeviceType.MOBILE, "Google Nexus 5", "Android 7.0", "Element", "1.5.0"), + DeviceExtendedInfo(DeviceType.MOBILE, "Google (Nexus) 5", "Android 7.0", "Element", "1.5.0"), + DeviceExtendedInfo(DeviceType.MOBILE, "Google (Nexus) (5)", "Android 7.0", "Element", "1.5.0"), + DeviceExtendedInfo(DeviceType.MOBILE, "SM-A510F Build/MMB29", "Android 6.0.1", "Element", "1.0.0"), + DeviceExtendedInfo(DeviceType.MOBILE, "SM-G610M Build/NRD90M", "Android 7.0", "Element", "1.0.0"), ) private val A_USER_AGENT_LIST_FOR_IOS = listOf( @@ -48,10 +48,10 @@ private val A_USER_AGENT_LIST_FOR_IOS = listOf( "Element/1.8.21 (iPad Pro (12.9-inch) (3rd generation); iOS 15.2; Scale/3.00)", ) private val AN_EXPECTED_RESULT_LIST_FOR_IOS = listOf( - DeviceUserAgent(DeviceType.MOBILE, "iPhone", "iOS 15.2", "Element", "1.8.21"), - DeviceUserAgent(DeviceType.MOBILE, "iPhone XS Max", "iOS 15.2", "Element", "1.8.21"), - DeviceUserAgent(DeviceType.MOBILE, "iPad Pro (11-inch)", "iOS 15.2", "Element", "1.8.21"), - DeviceUserAgent(DeviceType.MOBILE, "iPad Pro (12.9-inch) (3rd generation)", "iOS 15.2", + DeviceExtendedInfo(DeviceType.MOBILE, "iPhone", "iOS 15.2", "Element", "1.8.21"), + DeviceExtendedInfo(DeviceType.MOBILE, "iPhone XS Max", "iOS 15.2", "Element", "1.8.21"), + DeviceExtendedInfo(DeviceType.MOBILE, "iPad Pro (11-inch)", "iOS 15.2", "Element", "1.8.21"), + DeviceExtendedInfo(DeviceType.MOBILE, "iPad Pro (12.9-inch) (3rd generation)", "iOS 15.2", "Element", "1.8.21"), ) @@ -61,8 +61,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( - DeviceUserAgent(DeviceType.DESKTOP, "Electron", "Macintosh", null, null), - DeviceUserAgent(DeviceType.DESKTOP, "Electron", "Windows NT 10.0", null, null), + DeviceExtendedInfo(DeviceType.DESKTOP, "Electron", "Macintosh", null, null), + DeviceExtendedInfo(DeviceType.DESKTOP, "Electron", "Windows NT 10.0", null, null), ) private val A_USER_AGENT_LIST_FOR_WEB = listOf( @@ -77,15 +77,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( - DeviceUserAgent(DeviceType.WEB, "Chrome", "Macintosh", null, null), - DeviceUserAgent(DeviceType.WEB, "Chrome", "Windows NT 10.0", null, null), - DeviceUserAgent(DeviceType.WEB, "Firefox", "Macintosh", null, null), - DeviceUserAgent(DeviceType.WEB, "Safari", "Macintosh", null, null), - DeviceUserAgent(DeviceType.WEB, "Chrome", "Android 9", null, null), - DeviceUserAgent(DeviceType.WEB, "Safari", "iPad", null, null), - DeviceUserAgent(DeviceType.WEB, "Safari", "iPhone", null, null), - DeviceUserAgent(DeviceType.WEB, "Firefox", "Windows NT 6.0", null, null), - DeviceUserAgent(DeviceType.WEB, "Edge", "Windows NT 10.0", null, null), + DeviceExtendedInfo(DeviceType.WEB, "Chrome", "Macintosh", null, null), + DeviceExtendedInfo(DeviceType.WEB, "Chrome", "Windows NT 10.0", null, null), + DeviceExtendedInfo(DeviceType.WEB, "Firefox", "Macintosh", null, null), + DeviceExtendedInfo(DeviceType.WEB, "Safari", "Macintosh", null, null), + DeviceExtendedInfo(DeviceType.WEB, "Chrome", "Android 9", null, null), + DeviceExtendedInfo(DeviceType.WEB, "Safari", "iPad", null, null), + DeviceExtendedInfo(DeviceType.WEB, "Safari", "iPhone", null, null), + DeviceExtendedInfo(DeviceType.WEB, "Firefox", "Windows NT 6.0", null, null), + DeviceExtendedInfo(DeviceType.WEB, "Edge", "Windows NT 10.0", null, null), ) private val AN_UNKNOWN_USER_AGENT_LIST = listOf( @@ -93,8 +93,8 @@ private val AN_UNKNOWN_USER_AGENT_LIST = listOf( "Curl Client/1.0", ) private val AN_UNKNOWN_USER_AGENT_EXPECTED_RESULT_LIST = listOf( - DeviceUserAgent(DeviceType.UNKNOWN, null, null, null, null), - DeviceUserAgent(DeviceType.UNKNOWN, null, null, null, null), + DeviceExtendedInfo(DeviceType.UNKNOWN, null, null, null, null), + DeviceExtendedInfo(DeviceType.UNKNOWN, null, null, null, null), ) class ParseDeviceUserAgentUseCaseTest { diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt index 88828419f4..bdfa259eb5 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt @@ -17,7 +17,7 @@ package im.vector.app.features.settings.devices.v2.filter import im.vector.app.features.settings.devices.v2.DeviceFullInfo -import im.vector.app.features.settings.devices.v2.DeviceUserAgent +import im.vector.app.features.settings.devices.v2.DeviceExtendedInfo import im.vector.app.features.settings.devices.v2.list.DeviceType import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldContainAll @@ -37,7 +37,7 @@ private val activeVerifiedDevice = DeviceFullInfo( roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, isInactive = false, isCurrentDevice = true, - deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) + deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE) ) private val inactiveVerifiedDevice = DeviceFullInfo( deviceInfo = DeviceInfo(deviceId = "INACTIVE_VERIFIED_DEVICE"), @@ -49,7 +49,7 @@ private val inactiveVerifiedDevice = DeviceFullInfo( roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, isInactive = true, isCurrentDevice = false, - deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) + deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE) ) private val activeUnverifiedDevice = DeviceFullInfo( deviceInfo = DeviceInfo(deviceId = "ACTIVE_UNVERIFIED_DEVICE"), @@ -61,7 +61,7 @@ private val activeUnverifiedDevice = DeviceFullInfo( roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning, isInactive = false, isCurrentDevice = false, - deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) + deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE) ) private val inactiveUnverifiedDevice = DeviceFullInfo( deviceInfo = DeviceInfo(deviceId = "INACTIVE_UNVERIFIED_DEVICE"), @@ -73,7 +73,7 @@ private val inactiveUnverifiedDevice = DeviceFullInfo( roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning, isInactive = true, isCurrentDevice = false, - deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) + deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE) ) private val devices = listOf( diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt index 425935d065..a82b6311b3 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt @@ -19,7 +19,7 @@ package im.vector.app.features.settings.devices.v2.overview import androidx.lifecycle.MutableLiveData import androidx.lifecycle.asFlow import im.vector.app.features.settings.devices.v2.DeviceFullInfo -import im.vector.app.features.settings.devices.v2.DeviceUserAgent +import im.vector.app.features.settings.devices.v2.DeviceExtendedInfo import im.vector.app.features.settings.devices.v2.ParseDeviceUserAgentUseCase import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase import im.vector.app.features.settings.devices.v2.list.DeviceType @@ -92,7 +92,7 @@ class GetDeviceFullInfoUseCaseTest { val isInactive = false val isCurrentDevice = true every { checkIfSessionIsInactiveUseCase.execute(any()) } returns isInactive - every { parseDeviceUserAgentUseCase.execute(any()) } returns DeviceUserAgent(DeviceType.MOBILE) + every { parseDeviceUserAgentUseCase.execute(any()) } returns DeviceExtendedInfo(DeviceType.MOBILE) // When val deviceFullInfo = getDeviceFullInfoUseCase.execute(A_DEVICE_ID).firstOrNull() @@ -104,7 +104,7 @@ class GetDeviceFullInfoUseCaseTest { roomEncryptionTrustLevel = trustLevel, isInactive = isInactive, isCurrentDevice = isCurrentDevice, - deviceUserAgent = DeviceUserAgent(DeviceType.MOBILE) + deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE) ) verify { fakeActiveSessionHolder.instance.getSafeActiveSession() } verify { getCurrentSessionCrossSigningInfoUseCase.execute() } From 81e8ddf832097d7d1820d98fd08d8bdb49722828 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 29 Sep 2022 17:40:26 +0300 Subject: [PATCH 13/16] Code review fixes. --- .../settings/devices/v2/ParseDeviceUserAgentUseCase.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt index a4fe5f4096..eb70addbb9 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt @@ -113,8 +113,7 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { } private fun isMobile(browserSegments: List): Boolean { - return browserSegments.lastOrNull()?.startsWith("Safari").orFalse() && - browserSegments.getOrNull(browserSegments.size - 2)?.startsWith("Mobile").orFalse() + return browserSegments.getOrNull(browserSegments.size - 2)?.startsWith("Mobile").orFalse() } private fun getMobileBrowserName(browserSegments: List): String? { From 0f8637bc7a9ffe459eae765436f56190a0cbc200 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 29 Sep 2022 17:57:22 +0300 Subject: [PATCH 14/16] Lint fix. --- .../settings/devices/v2/filter/FilterDevicesUseCaseTest.kt | 2 +- .../devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt index bdfa259eb5..3448c7324d 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt @@ -16,8 +16,8 @@ package im.vector.app.features.settings.devices.v2.filter -import im.vector.app.features.settings.devices.v2.DeviceFullInfo import im.vector.app.features.settings.devices.v2.DeviceExtendedInfo +import im.vector.app.features.settings.devices.v2.DeviceFullInfo import im.vector.app.features.settings.devices.v2.list.DeviceType import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldContainAll diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt index a82b6311b3..a77f8e81fd 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt @@ -18,8 +18,8 @@ package im.vector.app.features.settings.devices.v2.overview import androidx.lifecycle.MutableLiveData import androidx.lifecycle.asFlow -import im.vector.app.features.settings.devices.v2.DeviceFullInfo import im.vector.app.features.settings.devices.v2.DeviceExtendedInfo +import im.vector.app.features.settings.devices.v2.DeviceFullInfo import im.vector.app.features.settings.devices.v2.ParseDeviceUserAgentUseCase import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase import im.vector.app.features.settings.devices.v2.list.DeviceType From bf4576d1556fe20336d405abbe88f9d7187f6175 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 30 Sep 2022 13:21:44 +0300 Subject: [PATCH 15/16] Code review fixes. --- .../devices/v2/ParseDeviceUserAgentUseCase.kt | 25 ++++++++++++++++--- .../v2/ParseDeviceUserAgentUseCaseTest.kt | 22 ++++++++-------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt index eb70addbb9..82c6edcbdf 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt @@ -48,7 +48,13 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { deviceModel = deviceInfoSegments.getOrNull(0) deviceOperatingSystem = deviceInfoSegments.getOrNull(1) } - return DeviceExtendedInfo(DeviceType.MOBILE, deviceModel, deviceOperatingSystem, appName, appVersion) + return DeviceExtendedInfo( + deviceType = DeviceType.MOBILE, + deviceModel = deviceModel, + deviceOperatingSystem = deviceOperatingSystem, + clientName = appName, + clientVersion = appVersion + ) } private fun parseIosUserAgent(userAgent: String): DeviceExtendedInfo { @@ -57,7 +63,13 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { val deviceInfoSegments = userAgent.substringAfter("(").substringBeforeLast(")").split("; ") val deviceModel = deviceInfoSegments.getOrNull(0) val deviceOperatingSystem = deviceInfoSegments.getOrNull(1) - return DeviceExtendedInfo(DeviceType.MOBILE, deviceModel, deviceOperatingSystem, appName, appVersion) + return DeviceExtendedInfo( + deviceType = DeviceType.MOBILE, + deviceModel = deviceModel, + deviceOperatingSystem = deviceOperatingSystem, + clientName = appName, + clientVersion = appVersion + ) } private fun parseDesktopUserAgent(userAgent: String): DeviceExtendedInfo { @@ -86,7 +98,14 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { } else { deviceOperatingSystemSegments.getOrNull(0) } - return DeviceExtendedInfo(DeviceType.DESKTOP, browserName, deviceOperatingSystem, null, null) + return DeviceExtendedInfo( + deviceType = DeviceType.DESKTOP, + deviceModel = null, + deviceOperatingSystem = deviceOperatingSystem, + clientName = null, + clientVersion = null, + browser = browserName + ) } private fun parseWebUserAgent(userAgent: String): DeviceExtendedInfo { diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt index 0cb0019a2a..11f319f472 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt @@ -61,8 +61,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, "Electron", "Macintosh", null, null), - DeviceExtendedInfo(DeviceType.DESKTOP, "Electron", "Windows NT 10.0", null, null), + DeviceExtendedInfo(DeviceType.DESKTOP, null, "Macintosh", null, null, "Electron"), + DeviceExtendedInfo(DeviceType.DESKTOP, null, "Windows NT 10.0", null, null, "Electron"), ) private val A_USER_AGENT_LIST_FOR_WEB = listOf( @@ -77,15 +77,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, "Chrome", "Macintosh", null, null), - DeviceExtendedInfo(DeviceType.WEB, "Chrome", "Windows NT 10.0", null, null), - DeviceExtendedInfo(DeviceType.WEB, "Firefox", "Macintosh", null, null), - DeviceExtendedInfo(DeviceType.WEB, "Safari", "Macintosh", null, null), - DeviceExtendedInfo(DeviceType.WEB, "Chrome", "Android 9", null, null), - DeviceExtendedInfo(DeviceType.WEB, "Safari", "iPad", null, null), - DeviceExtendedInfo(DeviceType.WEB, "Safari", "iPhone", null, null), - DeviceExtendedInfo(DeviceType.WEB, "Firefox", "Windows NT 6.0", null, null), - DeviceExtendedInfo(DeviceType.WEB, "Edge", "Windows NT 10.0", null, null), + DeviceExtendedInfo(DeviceType.WEB, null, "Macintosh", null, null, "Chrome"), + DeviceExtendedInfo(DeviceType.WEB, null, "Windows NT 10.0", null, null, "Chrome"), + DeviceExtendedInfo(DeviceType.WEB, null, "Macintosh", null, null, "Firefox"), + DeviceExtendedInfo(DeviceType.WEB, null, "Macintosh", null, null, "Safari"), + DeviceExtendedInfo(DeviceType.WEB, null, "Android 9", null, null, "Chrome"), + DeviceExtendedInfo(DeviceType.WEB, null, "iPad", null, null, "Safari"), + DeviceExtendedInfo(DeviceType.WEB, null, "iPhone", null, null, "Safari"), + DeviceExtendedInfo(DeviceType.WEB, null, "Windows NT 6.0", null, null, "Firefox"), + DeviceExtendedInfo(DeviceType.WEB, null, "Windows NT 10.0", null, null, "Edge"), ) private val AN_UNKNOWN_USER_AGENT_LIST = listOf( From ea8dc45796413399b3af06b67db9ef7eecfaf2ff Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 30 Sep 2022 17:08:35 +0300 Subject: [PATCH 16/16] Parse browser version. --- .../settings/devices/v2/DeviceExtendedInfo.kt | 4 -- .../devices/v2/ParseDeviceUserAgentUseCase.kt | 44 +++++++++++++++---- .../v2/ParseDeviceUserAgentUseCaseTest.kt | 22 +++++----- 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceExtendedInfo.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceExtendedInfo.kt index c9d27d093b..24e4606ca7 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceExtendedInfo.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceExtendedInfo.kt @@ -39,8 +39,4 @@ data class DeviceExtendedInfo( * i.e. 1.5.0. */ val clientVersion: String? = null, - /** - * i.e. Chrome. - */ - val browser: String? = null, ) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt index 82c6edcbdf..f5f1782d82 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt @@ -74,21 +74,38 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { private fun parseDesktopUserAgent(userAgent: String): DeviceExtendedInfo { val browserSegments = userAgent.split(" ") - val browserName = when { + val (browserName, browserVersion) = when { isFirefox(browserSegments) -> { - "Firefox" + Pair("Firefox", getBrowserVersion(browserSegments, "Firefox")) } isEdge(browserSegments) -> { - "Edge" + Pair("Edge", getBrowserVersion(browserSegments, "Edge")) } isMobile(browserSegments) -> { - getMobileBrowserName(browserSegments) + when (val name = getMobileBrowserName(browserSegments)) { + null -> { + Pair(null, null) + } + "Safari" -> { + Pair(name, getBrowserVersion(browserSegments, "Version")) + } + else -> { + Pair(name, getBrowserVersion(browserSegments, name)) + } + } } isSafari(browserSegments) -> { - "Safari" + Pair("Safari", getBrowserVersion(browserSegments, "Version")) } else -> { - getRegularBrowserName(browserSegments) + when (val name = getRegularBrowserName(browserSegments)) { + null -> { + Pair(null, null) + } + else -> { + Pair(name, getBrowserVersion(browserSegments, name)) + } + } } } @@ -102,9 +119,8 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { deviceType = DeviceType.DESKTOP, deviceModel = null, deviceOperatingSystem = deviceOperatingSystem, - clientName = null, - clientVersion = null, - browser = browserName + clientName = browserName, + clientVersion = browserVersion, ) } @@ -122,6 +138,16 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { return browserSegments.lastOrNull()?.startsWith("Firefox").orFalse() } + private fun getBrowserVersion(browserSegments: List, browserName: String): String? { + // Chrome/104.0.3497.100 -> 104 + return browserSegments + .find { it.startsWith(browserName) } + ?.split("/") + ?.getOrNull(1) + ?.split(".") + ?.firstOrNull() + } + private fun isEdge(browserSegments: List): Boolean { return browserSegments.lastOrNull()?.startsWith("Edge").orFalse() } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt index 11f319f472..22a5a7614f 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt @@ -61,8 +61,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", null, null, "Electron"), - DeviceExtendedInfo(DeviceType.DESKTOP, null, "Windows NT 10.0", null, null, "Electron"), + DeviceExtendedInfo(DeviceType.DESKTOP, null, "Macintosh", "Electron", "20"), + DeviceExtendedInfo(DeviceType.DESKTOP, null, "Windows NT 10.0", "Electron", "20"), ) private val A_USER_AGENT_LIST_FOR_WEB = listOf( @@ -77,15 +77,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", null, null, "Chrome"), - DeviceExtendedInfo(DeviceType.WEB, null, "Windows NT 10.0", null, null, "Chrome"), - DeviceExtendedInfo(DeviceType.WEB, null, "Macintosh", null, null, "Firefox"), - DeviceExtendedInfo(DeviceType.WEB, null, "Macintosh", null, null, "Safari"), - DeviceExtendedInfo(DeviceType.WEB, null, "Android 9", null, null, "Chrome"), - DeviceExtendedInfo(DeviceType.WEB, null, "iPad", null, null, "Safari"), - DeviceExtendedInfo(DeviceType.WEB, null, "iPhone", null, null, "Safari"), - DeviceExtendedInfo(DeviceType.WEB, null, "Windows NT 6.0", null, null, "Firefox"), - DeviceExtendedInfo(DeviceType.WEB, null, "Windows NT 10.0", null, null, "Edge"), + DeviceExtendedInfo(DeviceType.WEB, null, "Macintosh", "Chrome", "104"), + DeviceExtendedInfo(DeviceType.WEB, null, "Windows NT 10.0", "Chrome", "104"), + DeviceExtendedInfo(DeviceType.WEB, null, "Macintosh", "Firefox", "39"), + DeviceExtendedInfo(DeviceType.WEB, null, "Macintosh", "Safari", "8"), + DeviceExtendedInfo(DeviceType.WEB, null, "Android 9", "Chrome", "69"), + DeviceExtendedInfo(DeviceType.WEB, null, "iPad", "Safari", "8"), + DeviceExtendedInfo(DeviceType.WEB, null, "iPhone", "Safari", "8"), + DeviceExtendedInfo(DeviceType.WEB, null, "Windows NT 6.0", "Firefox", "40"), + DeviceExtendedInfo(DeviceType.WEB, null, "Windows NT 10.0", "Edge", "12"), ) private val AN_UNKNOWN_USER_AGENT_LIST = listOf(