diff --git a/changelog.d/7294.wip b/changelog.d/7294.wip
new file mode 100644
index 00000000000..f163f6b680a
--- /dev/null
+++ b/changelog.d/7294.wip
@@ -0,0 +1 @@
+[Device Management] Render extended device info
diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 63c1f8a8bbf..61043a80e3c 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3309,6 +3309,13 @@
Session name
Session ID
Last activity
+ Application
+ Name
+ Version
+ URL
+ Browser
+ Model
+ Operating system
IP address
Rename session
Session name
diff --git a/vector/src/main/java/im/vector/app/core/session/clientinfo/SessionExtendedInfoConstants.kt b/vector/src/main/java/im/vector/app/core/session/clientinfo/SessionExtendedInfoConstants.kt
index 80f69df1f87..663a715e021 100644
--- a/vector/src/main/java/im/vector/app/core/session/clientinfo/SessionExtendedInfoConstants.kt
+++ b/vector/src/main/java/im/vector/app/core/session/clientinfo/SessionExtendedInfoConstants.kt
@@ -19,4 +19,4 @@ package im.vector.app.core.session.clientinfo
/**
* Prefix for the key account data event which holds client info.
*/
-const val MATRIX_CLIENT_INFO_KEY_PREFIX = "io.element.matrix_client_information."
+internal const val MATRIX_CLIENT_INFO_KEY_PREFIX = "io.element.matrix_client_information."
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 445eb6226f7..a47ea7e9170 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
@@ -16,6 +16,8 @@
package im.vector.app.features.settings.devices.v2
+import im.vector.app.core.session.clientinfo.MatrixClientInfoContent
+import im.vector.app.features.settings.devices.v2.details.extended.DeviceExtendedInfo
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
@@ -27,4 +29,5 @@ data class DeviceFullInfo(
val isInactive: Boolean,
val isCurrentDevice: Boolean,
val deviceExtendedInfo: DeviceExtendedInfo,
+ val matrixClientInfo: MatrixClientInfoContent?,
)
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 0272bea351f..42e4cebe4cf 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
@@ -17,6 +17,7 @@
package im.vector.app.features.settings.devices.v2
import im.vector.app.core.di.ActiveSessionHolder
+import im.vector.app.core.session.clientinfo.GetMatrixClientInfoUseCase
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
@@ -27,6 +28,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emptyFlow
+import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
import org.matrix.android.sdk.flow.flow
@@ -39,6 +41,7 @@ class GetDeviceFullInfoListUseCase @Inject constructor(
private val getCurrentSessionCrossSigningInfoUseCase: GetCurrentSessionCrossSigningInfoUseCase,
private val filterDevicesUseCase: FilterDevicesUseCase,
private val parseDeviceUserAgentUseCase: ParseDeviceUserAgentUseCase,
+ private val getMatrixClientInfoUseCase: GetMatrixClientInfoUseCase,
) {
fun execute(filterType: DeviceManagerFilterType, excludeCurrentDevice: Boolean = false): Flow> {
@@ -48,7 +51,7 @@ class GetDeviceFullInfoListUseCase @Inject constructor(
session.flow().liveUserCryptoDevices(session.myUserId),
session.flow().liveMyDevicesInfo()
) { currentSessionCrossSigningInfo, cryptoList, infoList ->
- val deviceFullInfoList = convertToDeviceFullInfoList(currentSessionCrossSigningInfo, cryptoList, infoList)
+ val deviceFullInfoList = convertToDeviceFullInfoList(session, currentSessionCrossSigningInfo, cryptoList, infoList)
val excludedDeviceIds = if (excludeCurrentDevice) {
listOf(currentSessionCrossSigningInfo.deviceId)
} else {
@@ -62,6 +65,7 @@ class GetDeviceFullInfoListUseCase @Inject constructor(
}
private fun convertToDeviceFullInfoList(
+ session: Session,
currentSessionCrossSigningInfo: CurrentSessionCrossSigningInfo,
cryptoList: List,
infoList: List,
@@ -73,8 +77,20 @@ 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.getBestLastSeenUserAgent())
- DeviceFullInfo(deviceInfo, cryptoDeviceInfo, roomEncryptionTrustLevel, isInactive, isCurrentDevice, deviceUserAgent)
+ val deviceExtendedInfo = parseDeviceUserAgentUseCase.execute(deviceInfo.getBestLastSeenUserAgent())
+ val matrixClientInfo = deviceInfo.deviceId
+ ?.takeIf { it.isNotEmpty() }
+ ?.let { getMatrixClientInfoUseCase.execute(session, it) }
+
+ DeviceFullInfo(
+ deviceInfo = deviceInfo,
+ cryptoDeviceInfo = cryptoDeviceInfo,
+ roomEncryptionTrustLevel = roomEncryptionTrustLevel,
+ isInactive = isInactive,
+ isCurrentDevice = isCurrentDevice,
+ deviceExtendedInfo = deviceExtendedInfo,
+ matrixClientInfo = matrixClientInfo
+ )
}
}
}
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 f5f1782d826..20b76fd8149 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
@@ -16,6 +16,7 @@
package im.vector.app.features.settings.devices.v2
+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 javax.inject.Inject
@@ -139,13 +140,11 @@ class ParseDeviceUserAgentUseCase @Inject constructor() {
}
private fun getBrowserVersion(browserSegments: List, browserName: String): String? {
- // Chrome/104.0.3497.100 -> 104
+ // e.g Chrome/104.0.3497.100 -> 104.0.3497.100
return browserSegments
.find { it.startsWith(browserName) }
?.split("/")
?.getOrNull(1)
- ?.split(".")
- ?.firstOrNull()
}
private fun isEdge(browserSegments: List): Boolean {
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionApplicationIsVisibleUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionApplicationIsVisibleUseCase.kt
new file mode 100644
index 00000000000..0c790c2ef02
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionApplicationIsVisibleUseCase.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.details
+
+import im.vector.app.core.session.clientinfo.MatrixClientInfoContent
+import org.matrix.android.sdk.api.extensions.orFalse
+import javax.inject.Inject
+
+class CheckIfSectionApplicationIsVisibleUseCase @Inject constructor() {
+
+ fun execute(matrixClientInfoContent: MatrixClientInfoContent?): Boolean {
+ return matrixClientInfoContent?.name?.isNotEmpty().orFalse() ||
+ matrixClientInfoContent?.version?.isNotEmpty().orFalse() ||
+ matrixClientInfoContent?.url?.isNotEmpty().orFalse()
+ }
+}
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 25b5ddb0e83..471ef05c8dc 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 1fb5be4d78a..8d780925df6 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
@@ -23,17 +23,21 @@ import im.vector.app.R
import im.vector.app.core.date.DateFormatKind
import im.vector.app.core.date.VectorDateFormatter
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
class SessionDetailsController @Inject constructor(
private val checkIfSectionSessionIsVisibleUseCase: CheckIfSectionSessionIsVisibleUseCase,
private val checkIfSectionDeviceIsVisibleUseCase: CheckIfSectionDeviceIsVisibleUseCase,
+ private val checkIfSectionApplicationIsVisibleUseCase: CheckIfSectionApplicationIsVisibleUseCase,
private val stringProvider: StringProvider,
private val dateFormatter: VectorDateFormatter,
private val dimensionConverter: DimensionConverter,
-) : TypedEpoxyController() {
+) : TypedEpoxyController() {
var callback: Callback? = null
@@ -41,15 +45,22 @@ class SessionDetailsController @Inject constructor(
fun onItemLongClicked(content: String)
}
- override fun buildModels(data: DeviceInfo?) {
- data?.let { info ->
- val hasSectionSession = hasSectionSession(data)
+ override fun buildModels(data: DeviceFullInfo?) {
+ data?.let { fullInfo ->
+ val deviceInfo = fullInfo.deviceInfo
+ val matrixClientInfo = fullInfo.matrixClientInfo
+ val hasSectionSession = hasSectionSession(deviceInfo)
if (hasSectionSession) {
- buildSectionSession(info)
+ buildSectionSession(deviceInfo)
}
- if (hasSectionDevice(data)) {
- buildSectionDevice(info, addExtraTopMargin = hasSectionSession)
+ val hasApplicationSection = hasSectionApplication(matrixClientInfo)
+ if (hasApplicationSection && matrixClientInfo != null) {
+ buildSectionApplication(matrixClientInfo, addExtraTopMargin = hasSectionSession)
+ }
+
+ if (hasSectionDevice(fullInfo)) {
+ buildSectionDevice(fullInfo, addExtraTopMargin = hasSectionSession || hasApplicationSection)
}
}
}
@@ -83,39 +94,126 @@ class SessionDetailsController @Inject constructor(
}
private fun buildSectionSession(data: DeviceInfo) {
- val sessionName = data.displayName
- val sessionId = data.deviceId
- val sessionLastSeenTs = data.lastSeenTs
+ val sessionName = data.displayName.orEmpty()
+ val sessionId = data.deviceId.orEmpty()
+ val sessionLastSeenTs = data.lastSeenTs ?: -1
buildHeaderItem(R.string.device_manager_session_title)
- sessionName?.let {
- val hasDivider = sessionId != null || sessionLastSeenTs != null
- buildContentItem(R.string.device_manager_session_details_session_name, it, hasDivider)
+ if (sessionName.isNotEmpty()) {
+ val hasDivider = sessionId.isNotEmpty() || sessionLastSeenTs > 0
+ buildContentItem(R.string.device_manager_session_details_session_name, sessionName, hasDivider)
}
- sessionId?.let {
- val hasDivider = sessionLastSeenTs != null
- buildContentItem(R.string.device_manager_session_details_session_id, it, hasDivider)
+ if (sessionId.isNotEmpty()) {
+ val hasDivider = sessionLastSeenTs > 0
+ buildContentItem(R.string.device_manager_session_details_session_id, sessionId, hasDivider)
}
- sessionLastSeenTs?.let {
- val formattedDate = dateFormatter.format(it, DateFormatKind.MESSAGE_DETAIL)
+ if (sessionLastSeenTs > 0) {
+ val formattedDate = dateFormatter.format(sessionLastSeenTs, DateFormatKind.MESSAGE_DETAIL)
val hasDivider = false
buildContentItem(R.string.device_manager_session_details_session_last_activity, formattedDate, hasDivider)
}
}
- private fun hasSectionDevice(data: DeviceInfo): Boolean {
- return checkIfSectionDeviceIsVisibleUseCase.execute(data)
+ private fun hasSectionApplication(matrixClientInfoContent: MatrixClientInfoContent?): Boolean {
+ return checkIfSectionApplicationIsVisibleUseCase.execute(matrixClientInfoContent)
}
- private fun buildSectionDevice(data: DeviceInfo, addExtraTopMargin: Boolean) {
- val lastSeenIp = data.lastSeenIp
+ private fun buildSectionApplication(matrixClientInfoContent: MatrixClientInfoContent, addExtraTopMargin: Boolean) {
+ val name = matrixClientInfoContent.name.orEmpty()
+ val version = matrixClientInfoContent.version.orEmpty()
+ val url = matrixClientInfoContent.url.orEmpty()
+
+ buildHeaderItem(R.string.device_manager_session_details_application, addExtraTopMargin)
+
+ if (name.isNotEmpty()) {
+ val hasDivider = version.isNotEmpty() || url.isNotEmpty()
+ buildContentItem(R.string.device_manager_session_details_application_name, name, hasDivider)
+ }
+ if (version.isNotEmpty()) {
+ val hasDivider = url.isNotEmpty()
+ buildContentItem(R.string.device_manager_session_details_application_version, version, hasDivider)
+ }
+ if (url.isNotEmpty()) {
+ val hasDivider = false
+ buildContentItem(R.string.device_manager_session_details_application_url, url, hasDivider)
+ }
+ }
+ private fun hasSectionDevice(data: DeviceFullInfo): Boolean {
+ return checkIfSectionDeviceIsVisibleUseCase.execute(data)
+ }
+
+ private fun buildSectionDevice(data: DeviceFullInfo, addExtraTopMargin: Boolean) {
buildHeaderItem(R.string.device_manager_device_title, addExtraTopMargin)
- lastSeenIp?.let {
+ 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, it, hasDivider)
+ buildContentItem(R.string.device_manager_session_details_device_ip_address, lastSeenIp, hasDivider)
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsFragment.kt
index 5d7717e5f7b..c5296929a6c 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsFragment.kt
@@ -33,7 +33,7 @@ import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.platform.showOptimizedSnackbar
import im.vector.app.databinding.FragmentSessionDetailsBinding
-import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
+import im.vector.app.features.settings.devices.v2.DeviceFullInfo
import javax.inject.Inject
/**
@@ -92,16 +92,16 @@ class SessionDetailsFragment :
}
override fun invalidate() = withState(viewModel) { state ->
- if (state.deviceInfo is Success) {
- renderSessionDetails(state.deviceInfo.invoke())
+ if (state.deviceFullInfo is Success) {
+ renderSessionDetails(state.deviceFullInfo.invoke())
} else {
hideSessionDetails()
}
}
- private fun renderSessionDetails(deviceInfo: DeviceInfo) {
+ private fun renderSessionDetails(deviceFullInfo: DeviceFullInfo) {
views.sessionDetails.isVisible = true
- sessionDetailsController.setData(deviceInfo)
+ sessionDetailsController.setData(deviceFullInfo)
}
private fun hideSessionDetails() {
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewModel.kt
index c37858cc54d..44e10701a05 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewModel.kt
@@ -48,7 +48,7 @@ class SessionDetailsViewModel @AssistedInject constructor(
private fun observeSessionInfo(deviceId: String) {
getDeviceFullInfoUseCase.execute(deviceId)
- .onEach { setState { copy(deviceInfo = Success(it.deviceInfo)) } }
+ .onEach { setState { copy(deviceFullInfo = Success(it)) } }
.launchIn(viewModelScope)
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewState.kt
index 15868d3110e..d216bbda510 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewState.kt
@@ -19,11 +19,11 @@ package im.vector.app.features.settings.devices.v2.details
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.Uninitialized
-import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
+import im.vector.app.features.settings.devices.v2.DeviceFullInfo
data class SessionDetailsViewState(
val deviceId: String,
- val deviceInfo: Async = Uninitialized,
+ val deviceFullInfo: Async = Uninitialized,
) : MavericksState {
constructor(args: SessionDetailsArgs) : this(
deviceId = args.deviceId
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/details/extended/DeviceExtendedInfo.kt
similarity index 94%
rename from vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceExtendedInfo.kt
rename to vector/src/main/java/im/vector/app/features/settings/devices/v2/details/extended/DeviceExtendedInfo.kt
index 24e4606ca7d..5158ffbb8af 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/details/extended/DeviceExtendedInfo.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package im.vector.app.features.settings.devices.v2
+package im.vector.app.features.settings.devices.v2.details.extended
import im.vector.app.features.settings.devices.v2.list.DeviceType
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 42cd49b0728..140b55a30cc 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
@@ -18,6 +18,7 @@ package im.vector.app.features.settings.devices.v2.overview
import androidx.lifecycle.asFlow
import im.vector.app.core.di.ActiveSessionHolder
+import im.vector.app.core.session.clientinfo.GetMatrixClientInfoUseCase
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
@@ -36,6 +37,7 @@ class GetDeviceFullInfoUseCase @Inject constructor(
private val getEncryptionTrustLevelForDeviceUseCase: GetEncryptionTrustLevelForDeviceUseCase,
private val checkIfSessionIsInactiveUseCase: CheckIfSessionIsInactiveUseCase,
private val parseDeviceUserAgentUseCase: ParseDeviceUserAgentUseCase,
+ private val getMatrixClientInfoUseCase: GetMatrixClientInfoUseCase,
) {
fun execute(deviceId: String): Flow {
@@ -52,6 +54,10 @@ class GetDeviceFullInfoUseCase @Inject constructor(
val isInactive = checkIfSessionIsInactiveUseCase.execute(info.lastSeenTs ?: 0)
val isCurrentDevice = currentSessionCrossSigningInfo.deviceId == cryptoInfo.deviceId
val deviceUserAgent = parseDeviceUserAgentUseCase.execute(info.getBestLastSeenUserAgent())
+ val matrixClientInfo = info.deviceId
+ ?.takeIf { it.isNotEmpty() }
+ ?.let { getMatrixClientInfoUseCase.execute(session, it) }
+
DeviceFullInfo(
deviceInfo = info,
cryptoDeviceInfo = cryptoInfo,
@@ -59,6 +65,7 @@ class GetDeviceFullInfoUseCase @Inject constructor(
isInactive = isInactive,
isCurrentDevice = isCurrentDevice,
deviceExtendedInfo = deviceUserAgent,
+ matrixClientInfo = matrixClientInfo
)
} 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 c68394e7d7a..c5edfb868db 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,8 @@ package im.vector.app.features.settings.devices.v2
import android.os.SystemClock
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.test.MavericksTestRule
+import im.vector.app.core.session.clientinfo.MatrixClientInfoContent
+import im.vector.app.features.settings.devices.v2.details.extended.DeviceExtendedInfo
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
@@ -53,7 +55,7 @@ class DevicesViewModelTest {
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
private val getCurrentSessionCrossSigningInfoUseCase = mockk()
private val getDeviceFullInfoListUseCase = mockk()
- private val refreshDevicesUseCase = mockk()
+ private val refreshDevicesUseCase = mockk(relaxUnitFun = true)
private val refreshDevicesOnCryptoDevicesChangeUseCase = mockk()
private val checkIfCurrentSessionCanBeVerifiedUseCase = mockk()
@@ -245,7 +247,8 @@ class DevicesViewModelTest {
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted,
isInactive = false,
isCurrentDevice = true,
- deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE)
+ deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE),
+ matrixClientInfo = MatrixClientInfoContent(),
)
val deviceFullInfo2 = DeviceFullInfo(
deviceInfo = mockk(),
@@ -253,7 +256,8 @@ class DevicesViewModelTest {
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning,
isInactive = true,
isCurrentDevice = false,
- deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE)
+ deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE),
+ matrixClientInfo = MatrixClientInfoContent(),
)
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 efeb7f91b8a..ebdb74b74d3 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
@@ -16,6 +16,9 @@
package im.vector.app.features.settings.devices.v2
+import im.vector.app.core.session.clientinfo.GetMatrixClientInfoUseCase
+import im.vector.app.core.session.clientinfo.MatrixClientInfoContent
+import im.vector.app.features.settings.devices.v2.details.extended.DeviceExtendedInfo
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
@@ -55,6 +58,7 @@ class GetDeviceFullInfoListUseCaseTest {
private val getCurrentSessionCrossSigningInfoUseCase = mockk()
private val filterDevicesUseCase = mockk()
private val parseDeviceUserAgentUseCase = mockk()
+ private val getMatrixClientInfoUseCase = mockk()
private val getDeviceFullInfoListUseCase = GetDeviceFullInfoListUseCase(
activeSessionHolder = fakeActiveSessionHolder.instance,
@@ -63,6 +67,7 @@ class GetDeviceFullInfoListUseCaseTest {
getCurrentSessionCrossSigningInfoUseCase = getCurrentSessionCrossSigningInfoUseCase,
filterDevicesUseCase = filterDevicesUseCase,
parseDeviceUserAgentUseCase = parseDeviceUserAgentUseCase,
+ getMatrixClientInfoUseCase = getMatrixClientInfoUseCase,
)
@Before
@@ -108,13 +113,17 @@ class GetDeviceFullInfoListUseCaseTest {
)
val deviceInfoList = listOf(deviceInfo1, deviceInfo2, deviceInfo3)
every { fakeFlowSession.liveMyDevicesInfo() } returns flowOf(deviceInfoList)
+ val matrixClientInfo1 = givenAMatrixClientInfo(A_DEVICE_ID_1)
+ val matrixClientInfo2 = givenAMatrixClientInfo(A_DEVICE_ID_2)
+ val matrixClientInfo3 = givenAMatrixClientInfo(A_DEVICE_ID_3)
val expectedResult1 = DeviceFullInfo(
deviceInfo = deviceInfo1,
cryptoDeviceInfo = cryptoDeviceInfo1,
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted,
isInactive = true,
isCurrentDevice = true,
- deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE)
+ deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE),
+ matrixClientInfo = matrixClientInfo1,
)
val expectedResult2 = DeviceFullInfo(
deviceInfo = deviceInfo2,
@@ -122,7 +131,8 @@ class GetDeviceFullInfoListUseCaseTest {
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted,
isInactive = false,
isCurrentDevice = false,
- deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE)
+ deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE),
+ matrixClientInfo = matrixClientInfo2,
)
val expectedResult3 = DeviceFullInfo(
deviceInfo = deviceInfo3,
@@ -130,7 +140,8 @@ class GetDeviceFullInfoListUseCaseTest {
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning,
isInactive = false,
isCurrentDevice = false,
- deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE)
+ deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE),
+ matrixClientInfo = matrixClientInfo3,
)
val expectedResult = listOf(expectedResult3, expectedResult2, expectedResult1)
every { filterDevicesUseCase.execute(any(), any()) } returns expectedResult
@@ -152,6 +163,9 @@ class GetDeviceFullInfoListUseCaseTest {
checkIfSessionIsInactiveUseCase.execute(A_TIMESTAMP_1)
checkIfSessionIsInactiveUseCase.execute(A_TIMESTAMP_2)
checkIfSessionIsInactiveUseCase.execute(A_TIMESTAMP_3)
+ getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, A_DEVICE_ID_1)
+ getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, A_DEVICE_ID_2)
+ getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, A_DEVICE_ID_3)
}
}
@@ -201,4 +215,10 @@ class GetDeviceFullInfoListUseCaseTest {
return deviceInfo
}
+
+ private fun givenAMatrixClientInfo(deviceId: String): MatrixClientInfoContent {
+ val matrixClientInfo = mockk()
+ every { getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, deviceId) } returns matrixClientInfo
+ return matrixClientInfo
+ }
}
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 22a5a7614f5..19caee1db7e 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
@@ -16,6 +16,7 @@
package im.vector.app.features.settings.devices.v2
+import im.vector.app.features.settings.devices.v2.details.extended.DeviceExtendedInfo
import im.vector.app.features.settings.devices.v2.list.DeviceType
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
@@ -61,8 +62,8 @@ private val A_USER_AGENT_LIST_FOR_DESKTOP = listOf(
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) ElementNightly/2022091301 Chrome/104.0.5112.102 Electron/20.1.1 Safari/537.36",
)
private val AN_EXPECTED_RESULT_LIST_FOR_DESKTOP = listOf(
- DeviceExtendedInfo(DeviceType.DESKTOP, null, "Macintosh", "Electron", "20"),
- DeviceExtendedInfo(DeviceType.DESKTOP, null, "Windows NT 10.0", "Electron", "20"),
+ DeviceExtendedInfo(DeviceType.DESKTOP, null, "Macintosh", "Electron", "20.1.1"),
+ DeviceExtendedInfo(DeviceType.DESKTOP, null, "Windows NT 10.0", "Electron", "20.1.1"),
)
private val A_USER_AGENT_LIST_FOR_WEB = listOf(
@@ -77,15 +78,15 @@ private val A_USER_AGENT_LIST_FOR_WEB = listOf(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246",
)
private val AN_EXPECTED_RESULT_LIST_FOR_WEB = listOf(
- DeviceExtendedInfo(DeviceType.WEB, null, "Macintosh", "Chrome", "104"),
- 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"),
+ DeviceExtendedInfo(DeviceType.WEB, null, "Macintosh", "Chrome", "104.0.5112.102"),
+ DeviceExtendedInfo(DeviceType.WEB, null, "Windows NT 10.0", "Chrome", "104.0.5112.102"),
+ DeviceExtendedInfo(DeviceType.WEB, null, "Macintosh", "Firefox", "39.0"),
+ DeviceExtendedInfo(DeviceType.WEB, null, "Macintosh", "Safari", "8.0.3"),
+ DeviceExtendedInfo(DeviceType.WEB, null, "Android 9", "Chrome", "69.0.3497.100"),
+ DeviceExtendedInfo(DeviceType.WEB, null, "iPad", "Safari", "8.0"),
+ DeviceExtendedInfo(DeviceType.WEB, null, "iPhone", "Safari", "8.0"),
+ DeviceExtendedInfo(DeviceType.WEB, null, "Windows NT 6.0", "Firefox", "40.0"),
+ DeviceExtendedInfo(DeviceType.WEB, null, "Windows NT 10.0", "Edge", "12.246"),
)
private val AN_UNKNOWN_USER_AGENT_LIST = listOf(
diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionApplicationIsVisibleUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionApplicationIsVisibleUseCaseTest.kt
new file mode 100644
index 00000000000..23b02088b03
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionApplicationIsVisibleUseCaseTest.kt
@@ -0,0 +1,145 @@
+/*
+ * 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.details
+
+import im.vector.app.core.session.clientinfo.MatrixClientInfoContent
+import org.amshove.kluent.shouldBeEqualTo
+import org.junit.Test
+
+private const val AN_APP_NAME = "app-name"
+private const val AN_APP_VERSION = "app-version"
+private const val AN_APP_URL = "app-url"
+
+class CheckIfSectionApplicationIsVisibleUseCaseTest {
+
+ private val checkIfSectionApplicationIsVisibleUseCase = CheckIfSectionApplicationIsVisibleUseCase()
+
+ @Test
+ fun `given client info with name, version or url when checking is application section is visible then it returns true`() {
+ // Given
+ val clientInfoList = listOf(
+ givenAClientInfo(
+ name = AN_APP_NAME,
+ version = null,
+ url = null,
+ ),
+ givenAClientInfo(
+ name = null,
+ version = AN_APP_VERSION,
+ url = null,
+ ),
+ givenAClientInfo(
+ name = null,
+ version = null,
+ url = AN_APP_URL,
+ ),
+ givenAClientInfo(
+ name = AN_APP_NAME,
+ version = AN_APP_VERSION,
+ url = null,
+ ),
+ givenAClientInfo(
+ name = AN_APP_NAME,
+ version = null,
+ url = AN_APP_URL,
+ ),
+ givenAClientInfo(
+ name = null,
+ version = AN_APP_VERSION,
+ url = AN_APP_URL,
+ ),
+ givenAClientInfo(
+ name = AN_APP_NAME,
+ version = AN_APP_VERSION,
+ url = AN_APP_URL,
+ ),
+ )
+
+ clientInfoList.forEach { clientInfo ->
+ // When
+ val result = checkIfSectionApplicationIsVisibleUseCase.execute(clientInfo)
+
+ // Then
+ result shouldBeEqualTo true
+ }
+ }
+
+ @Test
+ fun `given client info with missing application info when checking is application section is visible then it returns false`() {
+ // Given
+ val clientInfoList = listOf(
+ givenAClientInfo(
+ name = null,
+ version = null,
+ url = null,
+ ),
+ givenAClientInfo(
+ name = "",
+ version = null,
+ url = null,
+ ),
+ givenAClientInfo(
+ name = null,
+ version = "",
+ url = null,
+ ),
+ givenAClientInfo(
+ name = null,
+ version = null,
+ url = "",
+ ),
+ givenAClientInfo(
+ name = "",
+ version = "",
+ url = null,
+ ),
+ givenAClientInfo(
+ name = "",
+ version = null,
+ url = "",
+ ),
+ givenAClientInfo(
+ name = null,
+ version = "",
+ url = "",
+ ),
+ givenAClientInfo(
+ name = "",
+ version = "",
+ url = "",
+ ),
+ )
+
+ clientInfoList.forEach { clientInfo ->
+ // When
+ val result = checkIfSectionApplicationIsVisibleUseCase.execute(clientInfo)
+
+ // Then
+ result shouldBeEqualTo false
+ }
+ }
+
+ private fun givenAClientInfo(
+ name: String?,
+ version: String?,
+ url: String?,
+ ) = MatrixClientInfoContent(
+ name = name,
+ version = version,
+ url = url,
+ )
+}
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 b618c58b7e4..d124f68b1b2 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,44 +16,116 @@
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 kotlinx.coroutines.test.runTest
import org.amshove.kluent.shouldBeEqualTo
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`() = runTest {
- // 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(deviceFullInfo)
+
+ // Then
+ result shouldBeEqualTo true
+ }
+ }
+
+ @Test
+ 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 result = checkIfSectionDeviceIsVisibleUseCase.execute(deviceInfo)
+ // When
+ val result1 = checkIfSectionDeviceIsVisibleUseCase.execute(deviceFullInfo1)
+ val result2 = checkIfSectionDeviceIsVisibleUseCase.execute(deviceFullInfo2)
- // Then
- result shouldBeEqualTo true
+ // Then
+ result1 shouldBeEqualTo false
+ result2 shouldBeEqualTo false
+ }
}
@Test
- fun `given device info with empty or null Ip address when checking is device section is visible then it returns false`() = runTest {
+ 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 {
@@ -61,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,
+ )
+ }
}
diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionSessionIsVisibleUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionSessionIsVisibleUseCaseTest.kt
index 806c86d175f..a6d29f4fe12 100644
--- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionSessionIsVisibleUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionSessionIsVisibleUseCaseTest.kt
@@ -18,7 +18,6 @@ package im.vector.app.features.settings.devices.v2.details
import io.mockk.every
import io.mockk.mockk
-import kotlinx.coroutines.test.runTest
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
@@ -32,7 +31,7 @@ class CheckIfSectionSessionIsVisibleUseCaseTest {
private val checkIfSectionSessionIsVisibleUseCase = CheckIfSectionSessionIsVisibleUseCase()
@Test
- fun `given device info with name, id or lastSeenTs when checking is session section is visible then it returns true`() = runTest {
+ fun `given device info with name, id or lastSeenTs when checking is session section is visible then it returns true`() {
// Given
val deviceInfoList = listOf(
givenADeviceInfo(
@@ -82,7 +81,7 @@ class CheckIfSectionSessionIsVisibleUseCaseTest {
}
@Test
- fun `given device info with missing session info when checking is session section is visible then it returns true`() = runTest {
+ fun `given device info with missing session info when checking is session section is visible then it returns false`() {
// Given
val deviceInfoList = listOf(
givenADeviceInfo(
diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewModelTest.kt
index 572f39af316..5fdd2192266 100644
--- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewModelTest.kt
+++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsViewModelTest.kt
@@ -57,12 +57,10 @@ class SessionDetailsViewModelTest {
fun `given the viewModel has been initialized then viewState is updated with session info`() {
// Given
val deviceFullInfo = mockk()
- val deviceInfo = mockk()
- every { deviceFullInfo.deviceInfo } returns deviceInfo
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID) } returns flowOf(deviceFullInfo)
val expectedState = SessionDetailsViewState(
deviceId = A_SESSION_ID,
- deviceInfo = Success(deviceInfo)
+ deviceFullInfo = Success(deviceFullInfo)
)
// When
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 3448c7324d2..3a418cf2c07 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,9 @@
package im.vector.app.features.settings.devices.v2.filter
-import im.vector.app.features.settings.devices.v2.DeviceExtendedInfo
+import im.vector.app.core.session.clientinfo.MatrixClientInfoContent
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.amshove.kluent.shouldBeEqualTo
import org.amshove.kluent.shouldContainAll
@@ -37,7 +38,8 @@ private val activeVerifiedDevice = DeviceFullInfo(
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted,
isInactive = false,
isCurrentDevice = true,
- deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE)
+ deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE),
+ matrixClientInfo = MatrixClientInfoContent(),
)
private val inactiveVerifiedDevice = DeviceFullInfo(
deviceInfo = DeviceInfo(deviceId = "INACTIVE_VERIFIED_DEVICE"),
@@ -49,7 +51,8 @@ private val inactiveVerifiedDevice = DeviceFullInfo(
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted,
isInactive = true,
isCurrentDevice = false,
- deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE)
+ deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE),
+ matrixClientInfo = MatrixClientInfoContent(),
)
private val activeUnverifiedDevice = DeviceFullInfo(
deviceInfo = DeviceInfo(deviceId = "ACTIVE_UNVERIFIED_DEVICE"),
@@ -61,7 +64,8 @@ private val activeUnverifiedDevice = DeviceFullInfo(
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning,
isInactive = false,
isCurrentDevice = false,
- deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE)
+ deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE),
+ matrixClientInfo = MatrixClientInfoContent(),
)
private val inactiveUnverifiedDevice = DeviceFullInfo(
deviceInfo = DeviceInfo(deviceId = "INACTIVE_UNVERIFIED_DEVICE"),
@@ -73,7 +77,8 @@ private val inactiveUnverifiedDevice = DeviceFullInfo(
roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning,
isInactive = true,
isCurrentDevice = false,
- deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE)
+ deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE),
+ matrixClientInfo = MatrixClientInfoContent(),
)
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 a77f8e81fd3..2185c295d0f 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,9 +18,11 @@ 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.DeviceExtendedInfo
+import im.vector.app.core.session.clientinfo.GetMatrixClientInfoUseCase
+import im.vector.app.core.session.clientinfo.MatrixClientInfoContent
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.details.extended.DeviceExtendedInfo
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
@@ -57,6 +59,7 @@ class GetDeviceFullInfoUseCaseTest {
private val checkIfSessionIsInactiveUseCase = mockk()
private val fakeFlowLiveDataConversions = FakeFlowLiveDataConversions()
private val parseDeviceUserAgentUseCase = mockk()
+ private val getMatrixClientInfoUseCase = mockk()
private val getDeviceFullInfoUseCase = GetDeviceFullInfoUseCase(
activeSessionHolder = fakeActiveSessionHolder.instance,
@@ -64,6 +67,7 @@ class GetDeviceFullInfoUseCaseTest {
getEncryptionTrustLevelForDeviceUseCase = getEncryptionTrustLevelForDeviceUseCase,
checkIfSessionIsInactiveUseCase = checkIfSessionIsInactiveUseCase,
parseDeviceUserAgentUseCase = parseDeviceUserAgentUseCase,
+ getMatrixClientInfoUseCase = getMatrixClientInfoUseCase,
)
@Before
@@ -80,19 +84,14 @@ class GetDeviceFullInfoUseCaseTest {
fun `given current session and info for device when getting device info then the result is correct`() = runTest {
// Given
val currentSessionCrossSigningInfo = givenCurrentSessionCrossSigningInfo()
- val deviceInfo = DeviceInfo(
- lastSeenTs = A_TIMESTAMP,
- )
- fakeActiveSessionHolder.fakeSession.fakeCryptoService.myDevicesInfoWithIdLiveData = MutableLiveData(Optional(deviceInfo))
- fakeActiveSessionHolder.fakeSession.fakeCryptoService.myDevicesInfoWithIdLiveData.givenAsFlow()
- val cryptoDeviceInfo = CryptoDeviceInfo(deviceId = A_DEVICE_ID, userId = "")
- fakeActiveSessionHolder.fakeSession.fakeCryptoService.cryptoDeviceInfoWithIdLiveData = MutableLiveData(Optional(cryptoDeviceInfo))
- fakeActiveSessionHolder.fakeSession.fakeCryptoService.cryptoDeviceInfoWithIdLiveData.givenAsFlow()
+ val deviceInfo = givenADeviceInfo()
+ val cryptoDeviceInfo = givenACryptoDeviceInfo()
val trustLevel = givenTrustLevel(currentSessionCrossSigningInfo, cryptoDeviceInfo)
val isInactive = false
val isCurrentDevice = true
every { checkIfSessionIsInactiveUseCase.execute(any()) } returns isInactive
every { parseDeviceUserAgentUseCase.execute(any()) } returns DeviceExtendedInfo(DeviceType.MOBILE)
+ val matrixClientInfo = givenAMatrixClientInfo()
// When
val deviceFullInfo = getDeviceFullInfoUseCase.execute(A_DEVICE_ID).firstOrNull()
@@ -104,14 +103,18 @@ class GetDeviceFullInfoUseCaseTest {
roomEncryptionTrustLevel = trustLevel,
isInactive = isInactive,
isCurrentDevice = isCurrentDevice,
- deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE)
+ deviceExtendedInfo = DeviceExtendedInfo(DeviceType.MOBILE),
+ matrixClientInfo = matrixClientInfo,
)
- verify { fakeActiveSessionHolder.instance.getSafeActiveSession() }
- verify { getCurrentSessionCrossSigningInfoUseCase.execute() }
- verify { getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo) }
- verify { fakeActiveSessionHolder.fakeSession.fakeCryptoService.getMyDevicesInfoLive(A_DEVICE_ID).asFlow() }
- verify { fakeActiveSessionHolder.fakeSession.fakeCryptoService.getLiveCryptoDeviceInfoWithId(A_DEVICE_ID).asFlow() }
- verify { checkIfSessionIsInactiveUseCase.execute(A_TIMESTAMP) }
+ verify {
+ fakeActiveSessionHolder.instance.getSafeActiveSession()
+ getCurrentSessionCrossSigningInfoUseCase.execute()
+ getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo)
+ fakeActiveSessionHolder.fakeSession.fakeCryptoService.getMyDevicesInfoLive(A_DEVICE_ID).asFlow()
+ fakeActiveSessionHolder.fakeSession.fakeCryptoService.getLiveCryptoDeviceInfoWithId(A_DEVICE_ID).asFlow()
+ checkIfSessionIsInactiveUseCase.execute(A_TIMESTAMP)
+ getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, A_DEVICE_ID)
+ }
}
@Test
@@ -161,4 +164,27 @@ class GetDeviceFullInfoUseCaseTest {
every { getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo) } returns trustLevel
return trustLevel
}
+
+ private fun givenADeviceInfo(): DeviceInfo {
+ val deviceInfo = DeviceInfo(
+ deviceId = A_DEVICE_ID,
+ lastSeenTs = A_TIMESTAMP,
+ )
+ fakeActiveSessionHolder.fakeSession.fakeCryptoService.myDevicesInfoWithIdLiveData = MutableLiveData(Optional(deviceInfo))
+ fakeActiveSessionHolder.fakeSession.fakeCryptoService.myDevicesInfoWithIdLiveData.givenAsFlow()
+ return deviceInfo
+ }
+
+ private fun givenACryptoDeviceInfo(): CryptoDeviceInfo {
+ val cryptoDeviceInfo = CryptoDeviceInfo(deviceId = A_DEVICE_ID, userId = "")
+ fakeActiveSessionHolder.fakeSession.fakeCryptoService.cryptoDeviceInfoWithIdLiveData = MutableLiveData(Optional(cryptoDeviceInfo))
+ fakeActiveSessionHolder.fakeSession.fakeCryptoService.cryptoDeviceInfoWithIdLiveData.givenAsFlow()
+ return cryptoDeviceInfo
+ }
+
+ private fun givenAMatrixClientInfo(): MatrixClientInfoContent {
+ val matrixClientInfo = mockk()
+ every { getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, A_DEVICE_ID) } returns matrixClientInfo
+ return matrixClientInfo
+ }
}