Rendering Device section with extended info

This commit is contained in:
Maxime NATUREL 2022-10-06 14:04:17 +02:00
parent 25a3d831f1
commit fdb61e26ee
4 changed files with 203 additions and 25 deletions

View file

@ -3313,6 +3313,9 @@
<string name="device_manager_session_details_application_name">Name</string>
<string name="device_manager_session_details_application_version">Version</string>
<string name="device_manager_session_details_application_url">URL</string>
<string name="device_manager_session_details_device_browser">Browser</string>
<string name="device_manager_session_details_device_model">Model</string>
<string name="device_manager_session_details_device_operating_system">Operating system</string>
<string name="device_manager_session_details_device_ip_address">IP address</string>
<string name="device_manager_session_rename">Rename session</string>
<string name="device_manager_session_rename_edit_hint">Session name</string>

View file

@ -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()
}
}

View file

@ -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)

View file

@ -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<DeviceFullInfo>()
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,
)
}
}