From eaf7da8e6e589b229c7b85a985809866ff530158 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 26 Aug 2022 12:59:01 +0300 Subject: [PATCH] Render other sessions. --- .../v2/VectorSettingsDevicesFragment.kt | 52 ++++++++++----- .../devices/v2/list/CurrentSessionView.kt | 23 +++---- .../devices/v2/list/OtherSessionItem.kt | 65 +++++++++++++++++++ .../v2/list/OtherSessionsController.kt | 40 +++++++++++- .../devices/v2/list/OtherSessionsView.kt | 11 +++- .../devices/v2/list/SessionDeviceType.kt | 24 +++++++ .../devices/v2/list/SessionListConstants.kt | 19 ++++++ 7 files changed, 197 insertions(+), 37 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionItem.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionDeviceType.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionListConstants.kt diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt index 166d28f7d3..82efcc5db9 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt @@ -36,10 +36,10 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentSettingsDevicesBinding import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.crypto.verification.VerificationBottomSheet +import im.vector.app.features.settings.devices.DeviceFullInfo import im.vector.app.features.settings.devices.DevicesAction import im.vector.app.features.settings.devices.DevicesViewEvents import im.vector.app.features.settings.devices.DevicesViewModel -import im.vector.app.features.settings.devices.DevicesViewState import javax.inject.Inject /** @@ -124,35 +124,53 @@ class VectorSettingsDevicesFragment @Inject constructor() : VectorBaseFragment - val currentDeviceInfo = state.devices() - ?.firstOrNull { - it.deviceInfo.deviceId == state.myDeviceId - } + if (state.devices is Success) { + val devices = state.devices() + val currentDeviceInfo = devices?.firstOrNull { + it.deviceInfo.deviceId == state.myDeviceId + } + val otherDevices = devices?.filter { it.deviceInfo.deviceId != state.myDeviceId } - if (state.devices is Success && currentDeviceInfo != null) { - renderCurrentDevice(state.accountCrossSigningIsTrusted, !state.hasAccountCrossSigning, currentDeviceInfo.deviceInfo.displayName ?: "") + renderCurrentDevice(currentDeviceInfo) + renderOtherSessionsView(otherDevices) } else { hideCurrentSessionView() + hideOtherSessionsView() } handleRequestStatus(state.request) } + private fun renderOtherSessionsView(otherDevices: List?) { + if (otherDevices.isNullOrEmpty()) { + hideOtherSessionsView() + } else { + views.deviceListHeaderSectionOther.isVisible = true + views.deviceListOtherSessions.isVisible = true + views.deviceListOtherSessions.update(otherDevices) + } + } + + private fun hideOtherSessionsView() { + views.deviceListHeaderSectionOther.isVisible = false + views.deviceListOtherSessions.isVisible = false + } + + private fun renderCurrentDevice(currentDeviceInfo: DeviceFullInfo?) { + currentDeviceInfo?.let { + views.deviceListHeaderSectionCurrent.isVisible = true + views.deviceListCurrentSession.isVisible = true + views.deviceListCurrentSession.update(it) + } ?: run { + hideCurrentSessionView() + } + } + private fun hideCurrentSessionView() { views.deviceListHeaderSectionCurrent.isVisible = false views.deviceListCurrentSession.isVisible = false } - private fun renderCurrentDevice(accountCrossSigningIsTrusted: Boolean, legacyMode: Boolean, sessionName: String) { - views.deviceListHeaderSectionCurrent.isVisible = true - views.deviceListCurrentSession.isVisible = true - views.deviceListCurrentSession.update( - accountCrossSigningIsTrusted = accountCrossSigningIsTrusted, - legacyMode = legacyMode, - sessionName = sessionName - ) - } - private fun handleRequestStatus(unIgnoreRequest: Async) { views.waitingView.root.isVisible = when (unIgnoreRequest) { is Loading -> true diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CurrentSessionView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CurrentSessionView.kt index 6d7c96420b..c74dd9a7fa 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CurrentSessionView.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CurrentSessionView.kt @@ -22,9 +22,9 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isVisible import im.vector.app.R import im.vector.app.databinding.ViewCurrentSessionBinding -import im.vector.app.features.settings.devices.TrustUtils +import im.vector.app.features.settings.devices.DeviceFullInfo import im.vector.app.features.themes.ThemeUtils -import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel +import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel class CurrentSessionView @JvmOverloads constructor( context: Context, @@ -39,21 +39,14 @@ class CurrentSessionView @JvmOverloads constructor( views = ViewCurrentSessionBinding.bind(this) } - fun update(accountCrossSigningIsTrusted: Boolean, legacyMode: Boolean, sessionName: String) { - renderDeviceInfo(sessionName) - renderVerificationStatus(accountCrossSigningIsTrusted, legacyMode) + fun update(currentDeviceInfo: DeviceFullInfo) { + renderDeviceInfo(currentDeviceInfo.deviceInfo.displayName ?: "") + renderVerificationStatus(currentDeviceInfo.trustLevelForShield) } - private fun renderVerificationStatus(accountCrossSigningIsTrusted: Boolean, legacyMode: Boolean) { - val deviceTrustLevel = DeviceTrustLevel(crossSigningVerified = accountCrossSigningIsTrusted, locallyVerified = true) - val shield = TrustUtils.shieldForTrust( - currentDevice = true, - trustMSK = accountCrossSigningIsTrusted, - legacyMode = legacyMode, - deviceTrustLevel = deviceTrustLevel - ) - views.currentSessionVerificationStatusImageView.render(shield) - if (deviceTrustLevel.crossSigningVerified) { + private fun renderVerificationStatus(trustLevelForShield: RoomEncryptionTrustLevel) { + views.currentSessionVerificationStatusImageView.render(trustLevelForShield) + if (trustLevelForShield == RoomEncryptionTrustLevel.Trusted) { renderCrossSigningVerified() } else { renderCrossSigningUnverified() diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionItem.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionItem.kt new file mode 100644 index 0000000000..f3b44572b3 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionItem.kt @@ -0,0 +1,65 @@ +/* + * 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.list + +import android.widget.ImageView +import android.widget.TextView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.core.ui.views.ShieldImageView +import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel + +@EpoxyModelClass +abstract class OtherSessionItem : VectorEpoxyModel(R.layout.item_other_session) { + + @EpoxyAttribute + var deviceType: SessionDeviceType = SessionDeviceType.UNKNOWN + + @EpoxyAttribute + var roomEncryptionTrustLevel: RoomEncryptionTrustLevel? = null + + @EpoxyAttribute + var sessionName: String? = null + + @EpoxyAttribute + var sessionDescription: String? = null + + override fun bind(holder: Holder) { + super.bind(holder) + holder.otherSessionDeviceTypeImageView.setImageResource( + when (deviceType) { + SessionDeviceType.MOBILE -> R.drawable.ic_device_type_mobile + SessionDeviceType.WEB -> R.drawable.ic_device_type_web + SessionDeviceType.DESKTOP -> R.drawable.ic_device_type_desktop + SessionDeviceType.UNKNOWN -> R.drawable.ic_device_type_unknown + } + ) + holder.otherSessionVerificationStatusImageView.render(roomEncryptionTrustLevel) + holder.otherSessionNameTextView.text = sessionName + holder.otherSessionDescriptionTextView.text = sessionDescription + } + + class Holder : VectorEpoxyHolder() { + val otherSessionDeviceTypeImageView by bind(R.id.otherSessionDeviceTypeImageView) + val otherSessionVerificationStatusImageView by bind(R.id.otherSessionVerificationStatusImageView) + val otherSessionNameTextView by bind(R.id.otherSessionNameTextView) + val otherSessionDescriptionTextView by bind(R.id.otherSessionDescriptionTextView) + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt index 5cf85d2069..24b9e539d8 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt @@ -17,12 +17,46 @@ package im.vector.app.features.settings.devices.v2.list import com.airbnb.epoxy.TypedEpoxyController -import im.vector.app.features.settings.devices.DevicesViewState +import im.vector.app.R +import im.vector.app.core.date.DateFormatKind +import im.vector.app.core.date.VectorDateFormatter +import im.vector.app.core.epoxy.noResultItem +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.settings.devices.DeviceFullInfo +import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import javax.inject.Inject -class OtherSessionsController @Inject constructor() : TypedEpoxyController() { +class OtherSessionsController @Inject constructor( + private val stringProvider: StringProvider, + private val dateFormatter: VectorDateFormatter, +) : TypedEpoxyController>() { - override fun buildModels(data: DevicesViewState?) { + override fun buildModels(data: List?) { + data ?: return + val host = this + if (data.isNullOrEmpty()) { + noResultItem { + id("empty") + text(host.stringProvider.getString(R.string.no_result_placeholder)) + } + } else { + data.take(NUMBER_OF_OTHER_DEVICES_TO_RENDER).forEach { device -> + val formattedLastActivityDate = host.dateFormatter.format(device.deviceInfo.lastSeenTs, DateFormatKind.DEFAULT_DATE_AND_TIME) + val description = if (device.trustLevelForShield == RoomEncryptionTrustLevel.Trusted) { + stringProvider.getString(R.string.device_manager_other_sessions_description_verified, formattedLastActivityDate) + } else { + stringProvider.getString(R.string.device_manager_other_sessions_description_unverified, formattedLastActivityDate) + } + + otherSessionItem { + id(device.deviceInfo.deviceId) + deviceType(SessionDeviceType.UNKNOWN) // TODO. We don't have this info yet. Update accordingly. + roomEncryptionTrustLevel(device.trustLevelForShield) + sessionName(device.deviceInfo.displayName) + sessionDescription(description) + } + } + } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt index 6275901556..3dd445beb6 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt @@ -19,17 +19,22 @@ package im.vector.app.features.settings.devices.v2.list import android.content.Context import android.util.AttributeSet import androidx.constraintlayout.widget.ConstraintLayout +import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R +import im.vector.app.core.extensions.configureWith import im.vector.app.databinding.ViewOtherSessionsBinding import im.vector.app.features.settings.devices.DeviceFullInfo -import timber.log.Timber +import javax.inject.Inject +@AndroidEntryPoint class OtherSessionsView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : ConstraintLayout(context, attrs, defStyleAttr) { + @Inject lateinit var otherSessionsController: OtherSessionsController + private val views: ViewOtherSessionsBinding init { @@ -38,6 +43,8 @@ class OtherSessionsView @JvmOverloads constructor( } fun update(devices: List) { - Timber.d("OtherSessionsView. Devices: " + devices.size) + views.otherSessionsRecyclerView.configureWith(otherSessionsController, hasFixedSize = true) + views.otherSessionsViewAllButton.text = context.getString(R.string.device_manager_other_sessions_view_all, devices.size) + otherSessionsController.setData(devices) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionDeviceType.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionDeviceType.kt new file mode 100644 index 0000000000..62e66f72f5 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionDeviceType.kt @@ -0,0 +1,24 @@ +/* + * 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.list + +enum class SessionDeviceType { + MOBILE, + WEB, + DESKTOP, + UNKNOWN, +} diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionListConstants.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionListConstants.kt new file mode 100644 index 0000000000..f4aadeb337 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionListConstants.kt @@ -0,0 +1,19 @@ +/* + * 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.list + +const val NUMBER_OF_OTHER_DEVICES_TO_RENDER = 5