diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index 0b17b64c4a..61043a80e3 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -3313,6 +3313,9 @@ Name Version URL + Browser + Model + Operating system IP address Rename session Session name diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionDeviceIsVisibleUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionDeviceIsVisibleUseCase.kt index 25b5ddb0e8..471ef05c8d 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionDeviceIsVisibleUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionDeviceIsVisibleUseCase.kt @@ -16,13 +16,36 @@ package im.vector.app.features.settings.devices.v2.details +import im.vector.app.features.settings.devices.v2.DeviceFullInfo +import im.vector.app.features.settings.devices.v2.details.extended.DeviceExtendedInfo +import im.vector.app.features.settings.devices.v2.list.DeviceType import org.matrix.android.sdk.api.extensions.orFalse -import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo import javax.inject.Inject class CheckIfSectionDeviceIsVisibleUseCase @Inject constructor() { - fun execute(deviceInfo: DeviceInfo): Boolean { - return deviceInfo.lastSeenIp?.isNotEmpty().orFalse() + fun execute(deviceFullInfo: DeviceFullInfo): Boolean { + val hasExtendedInfo = when (deviceFullInfo.deviceExtendedInfo.deviceType) { + DeviceType.MOBILE -> hasAnyDeviceExtendedInfoMobile(deviceFullInfo.deviceExtendedInfo) + DeviceType.WEB -> hasAnyDeviceExtendedInfoWeb(deviceFullInfo.deviceExtendedInfo) + DeviceType.DESKTOP -> hasAnyDeviceExtendedInfoDesktop(deviceFullInfo.deviceExtendedInfo) + DeviceType.UNKNOWN -> false + } + + return hasExtendedInfo || deviceFullInfo.deviceInfo.lastSeenIp?.isNotEmpty().orFalse() + } + + private fun hasAnyDeviceExtendedInfoMobile(deviceExtendedInfo: DeviceExtendedInfo): Boolean { + return deviceExtendedInfo.deviceModel?.isNotEmpty().orFalse() || + deviceExtendedInfo.deviceOperatingSystem?.isNotEmpty().orFalse() + } + + private fun hasAnyDeviceExtendedInfoWeb(deviceExtendedInfo: DeviceExtendedInfo): Boolean { + return deviceExtendedInfo.clientName?.isNotEmpty().orFalse() || + deviceExtendedInfo.deviceOperatingSystem?.isNotEmpty().orFalse() + } + + private fun hasAnyDeviceExtendedInfoDesktop(deviceExtendedInfo: DeviceExtendedInfo): Boolean { + return deviceExtendedInfo.deviceOperatingSystem?.isNotEmpty().orFalse() } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsController.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsController.kt index eb4f823889..8d780925df 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsController.kt @@ -26,6 +26,7 @@ import im.vector.app.core.resources.StringProvider import im.vector.app.core.session.clientinfo.MatrixClientInfoContent import im.vector.app.core.utils.DimensionConverter import im.vector.app.features.settings.devices.v2.DeviceFullInfo +import im.vector.app.features.settings.devices.v2.list.DeviceType import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo import javax.inject.Inject @@ -58,8 +59,8 @@ class SessionDetailsController @Inject constructor( buildSectionApplication(matrixClientInfo, addExtraTopMargin = hasSectionSession) } - if (hasSectionDevice(deviceInfo)) { - buildSectionDevice(deviceInfo, addExtraTopMargin = hasSectionSession || hasApplicationSection) + if (hasSectionDevice(fullInfo)) { + buildSectionDevice(fullInfo, addExtraTopMargin = hasSectionSession || hasApplicationSection) } } } @@ -139,15 +140,77 @@ class SessionDetailsController @Inject constructor( } } - private fun hasSectionDevice(data: DeviceInfo): Boolean { + private fun hasSectionDevice(data: DeviceFullInfo): Boolean { return checkIfSectionDeviceIsVisibleUseCase.execute(data) } - private fun buildSectionDevice(data: DeviceInfo, addExtraTopMargin: Boolean) { - val lastSeenIp = data.lastSeenIp.orEmpty() - + private fun buildSectionDevice(data: DeviceFullInfo, addExtraTopMargin: Boolean) { buildHeaderItem(R.string.device_manager_device_title, addExtraTopMargin) + when (data.deviceExtendedInfo.deviceType) { + DeviceType.MOBILE -> buildSectionDeviceMobile(data) + DeviceType.WEB -> buildSectionDeviceWeb(data) + DeviceType.DESKTOP -> buildSectionDeviceDesktop(data) + DeviceType.UNKNOWN -> buildSectionDeviceUnknown(data) + } + } + + private fun buildSectionDeviceWeb(data: DeviceFullInfo) { + val browserName = data.deviceExtendedInfo.clientName.orEmpty() + val browserVersion = data.deviceExtendedInfo.clientVersion.orEmpty() + val browser = "$browserName $browserVersion" + val operatingSystem = data.deviceExtendedInfo.deviceOperatingSystem.orEmpty() + val lastSeenIp = data.deviceInfo.lastSeenIp.orEmpty() + + if (browser.isNotEmpty()) { + val hasDivider = operatingSystem.isNotEmpty() || lastSeenIp.isNotEmpty() + buildContentItem(R.string.device_manager_session_details_device_browser, browser, hasDivider) + } + + if (operatingSystem.isNotEmpty()) { + val hasDivider = lastSeenIp.isNotEmpty() + buildContentItem(R.string.device_manager_session_details_device_operating_system, operatingSystem, hasDivider) + } + + buildIpAddressContentItem(lastSeenIp) + } + + private fun buildSectionDeviceDesktop(data: DeviceFullInfo) { + val operatingSystem = data.deviceExtendedInfo.deviceOperatingSystem.orEmpty() + val lastSeenIp = data.deviceInfo.lastSeenIp.orEmpty() + + if (operatingSystem.isNotEmpty()) { + val hasDivider = lastSeenIp.isNotEmpty() + buildContentItem(R.string.device_manager_session_details_device_operating_system, operatingSystem, hasDivider) + } + + buildIpAddressContentItem(lastSeenIp) + } + + private fun buildSectionDeviceMobile(data: DeviceFullInfo) { + val model = data.deviceExtendedInfo.deviceModel.orEmpty() + val operatingSystem = data.deviceExtendedInfo.deviceOperatingSystem.orEmpty() + val lastSeenIp = data.deviceInfo.lastSeenIp.orEmpty() + + if (model.isNotEmpty()) { + val hasDivider = operatingSystem.isNotEmpty() || lastSeenIp.isNotEmpty() + buildContentItem(R.string.device_manager_session_details_device_model, model, hasDivider) + } + + if (operatingSystem.isNotEmpty()) { + val hasDivider = lastSeenIp.isNotEmpty() + buildContentItem(R.string.device_manager_session_details_device_operating_system, operatingSystem, hasDivider) + } + + buildIpAddressContentItem(lastSeenIp) + } + + private fun buildSectionDeviceUnknown(data: DeviceFullInfo) { + val lastSeenIp = data.deviceInfo.lastSeenIp.orEmpty() + buildIpAddressContentItem(lastSeenIp) + } + + private fun buildIpAddressContentItem(lastSeenIp: String) { if (lastSeenIp.isNotEmpty()) { val hasDivider = false buildContentItem(R.string.device_manager_session_details_device_ip_address, lastSeenIp, hasDivider) diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionDeviceIsVisibleUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionDeviceIsVisibleUseCaseTest.kt index e5cd5f1647..d124f68b1b 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionDeviceIsVisibleUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionDeviceIsVisibleUseCaseTest.kt @@ -16,6 +16,9 @@ package im.vector.app.features.settings.devices.v2.details +import im.vector.app.features.settings.devices.v2.DeviceFullInfo +import im.vector.app.features.settings.devices.v2.details.extended.DeviceExtendedInfo +import im.vector.app.features.settings.devices.v2.list.DeviceType import io.mockk.every import io.mockk.mockk import org.amshove.kluent.shouldBeEqualTo @@ -23,36 +26,106 @@ import org.junit.Test import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo private const val AN_IP_ADDRESS = "ip-address" +private const val A_DEVICE_MODEL = "device-model" +private const val A_DEVICE_OPERATING_SYSTEM = "device-operating-system" +private const val A_CLIENT_NAME = "client-name" class CheckIfSectionDeviceIsVisibleUseCaseTest { private val checkIfSectionDeviceIsVisibleUseCase = CheckIfSectionDeviceIsVisibleUseCase() @Test - fun `given device info with Ip address when checking is device section is visible then it returns true`() { - // Given - val deviceInfo = givenADeviceInfo(AN_IP_ADDRESS) + fun `given device of any type with Ip address when checking if device section is visible then it returns true`() { + DeviceType.values().forEach { deviceType -> + // Given + val deviceExtendedInfo = givenAnExtendedDeviceInfo(deviceType) + val deviceFullInfo = givenADeviceFullInfo(deviceExtendedInfo) + val deviceInfo = givenADeviceInfo(ipAddress = AN_IP_ADDRESS) + every { deviceFullInfo.deviceInfo } returns deviceInfo - // When - val result = checkIfSectionDeviceIsVisibleUseCase.execute(deviceInfo) + // When + val result = checkIfSectionDeviceIsVisibleUseCase.execute(deviceFullInfo) - // Then - result shouldBeEqualTo true + // Then + result shouldBeEqualTo true + } } @Test - fun `given device info with empty or null Ip address when checking is device section is visible then it returns false`() { + fun `given device of any type with empty or null Ip address and no extended info when checking if device section is visible then it returns false`() { + DeviceType.values().forEach { deviceType -> + // Given + val deviceExtendedInfo = givenAnExtendedDeviceInfo(deviceType) + val deviceFullInfo1 = givenADeviceFullInfo(deviceExtendedInfo) + val deviceFullInfo2 = givenADeviceFullInfo(deviceExtendedInfo) + val deviceInfo1 = givenADeviceInfo(ipAddress = "") + val deviceInfo2 = givenADeviceInfo(ipAddress = null) + every { deviceFullInfo1.deviceInfo } returns deviceInfo1 + every { deviceFullInfo2.deviceInfo } returns deviceInfo2 + + // When + val result1 = checkIfSectionDeviceIsVisibleUseCase.execute(deviceFullInfo1) + val result2 = checkIfSectionDeviceIsVisibleUseCase.execute(deviceFullInfo2) + + // Then + result1 shouldBeEqualTo false + result2 shouldBeEqualTo false + } + } + + @Test + fun `given device of any type with extended info when checking if device section is visible then it returns true`() { // Given - val deviceInfo1 = givenADeviceInfo("") - val deviceInfo2 = givenADeviceInfo(null) + val deviceExtendedInfoList = listOf( + givenAnExtendedDeviceInfo( + DeviceType.MOBILE, + deviceModel = A_DEVICE_MODEL, + ), + givenAnExtendedDeviceInfo( + DeviceType.MOBILE, + deviceOperatingSystem = A_DEVICE_OPERATING_SYSTEM, + ), + givenAnExtendedDeviceInfo( + DeviceType.MOBILE, + deviceModel = A_DEVICE_MODEL, + deviceOperatingSystem = A_DEVICE_OPERATING_SYSTEM, + ), + givenAnExtendedDeviceInfo( + DeviceType.DESKTOP, + deviceOperatingSystem = A_DEVICE_OPERATING_SYSTEM, + ), + givenAnExtendedDeviceInfo( + DeviceType.WEB, + clientName = A_CLIENT_NAME, + ), + givenAnExtendedDeviceInfo( + DeviceType.WEB, + deviceOperatingSystem = A_DEVICE_OPERATING_SYSTEM, + ), + givenAnExtendedDeviceInfo( + DeviceType.WEB, + clientName = A_CLIENT_NAME, + deviceOperatingSystem = A_DEVICE_OPERATING_SYSTEM, + ), + ) - // When - val result1 = checkIfSectionDeviceIsVisibleUseCase.execute(deviceInfo1) - val result2 = checkIfSectionDeviceIsVisibleUseCase.execute(deviceInfo2) + deviceExtendedInfoList.forEach { deviceExtendedInfo -> + val deviceFullInfo = givenADeviceFullInfo(deviceExtendedInfo) + val deviceInfo = givenADeviceInfo(ipAddress = null) + every { deviceFullInfo.deviceInfo } returns deviceInfo - // Then - result1 shouldBeEqualTo false - result2 shouldBeEqualTo false + // When + val result = checkIfSectionDeviceIsVisibleUseCase.execute(deviceFullInfo) + + // Then + result shouldBeEqualTo true + } + } + + private fun givenADeviceFullInfo(deviceExtendedInfo: DeviceExtendedInfo): DeviceFullInfo { + val deviceFullInfo = mockk() + every { deviceFullInfo.deviceExtendedInfo } returns deviceExtendedInfo + return deviceFullInfo } private fun givenADeviceInfo(ipAddress: String?): DeviceInfo { @@ -60,4 +133,20 @@ class CheckIfSectionDeviceIsVisibleUseCaseTest { every { info.lastSeenIp } returns ipAddress return info } + + private fun givenAnExtendedDeviceInfo( + deviceType: DeviceType, + clientName: String? = null, + clientVersion: String? = null, + deviceOperatingSystem: String? = null, + deviceModel: String? = null, + ): DeviceExtendedInfo { + return DeviceExtendedInfo( + deviceType = deviceType, + clientName = clientName, + clientVersion = clientVersion, + deviceOperatingSystem = deviceOperatingSystem, + deviceModel = deviceModel, + ) + } }