diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index 63c1f8a8bb..0b17b64c4a 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -3309,6 +3309,10 @@ Session name Session ID Last activity + Application + Name + Version + URL IP address Rename session Session name 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 0000000000..cd93cb73db --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/CheckIfSectionApplicationIsVisibleUseCase.kt @@ -0,0 +1,31 @@ +/* + * 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() { + + // TODO add unit tests + 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/SessionDetailsController.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsController.kt index 1fb5be4d78..eb4f823889 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,20 @@ 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 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 +44,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(deviceInfo)) { + buildSectionDevice(deviceInfo, addExtraTopMargin = hasSectionSession || hasApplicationSection) } } } @@ -83,39 +93,64 @@ 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 hasSectionApplication(matrixClientInfoContent: MatrixClientInfoContent?): Boolean { + return checkIfSectionApplicationIsVisibleUseCase.execute(matrixClientInfoContent) + } + + 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: DeviceInfo): Boolean { return checkIfSectionDeviceIsVisibleUseCase.execute(data) } private fun buildSectionDevice(data: DeviceInfo, addExtraTopMargin: Boolean) { - val lastSeenIp = data.lastSeenIp + val lastSeenIp = data.lastSeenIp.orEmpty() buildHeaderItem(R.string.device_manager_device_title, addExtraTopMargin) - lastSeenIp?.let { + 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 5d7717e5f7..f518ab9382 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,6 +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 im.vector.app.features.settings.devices.v2.DeviceFullInfo import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo import javax.inject.Inject @@ -92,16 +93,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 c37858cc54..44e10701a0 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 15868d3110..d216bbda51 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/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 572f39af31..5fdd219226 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