Render other sessions.

This commit is contained in:
Onuray Sahin 2022-08-26 12:59:01 +03:00
parent eb86a4f33c
commit eaf7da8e6e
7 changed files with 197 additions and 37 deletions

View File

@ -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<F
}
override fun invalidate() = withState(viewModel) { state ->
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<DeviceFullInfo>?) {
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<Unit>) {
views.waitingView.root.isVisible = when (unIgnoreRequest) {
is Loading -> true

View File

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

View File

@ -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<OtherSessionItem.Holder>(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<ImageView>(R.id.otherSessionDeviceTypeImageView)
val otherSessionVerificationStatusImageView by bind<ShieldImageView>(R.id.otherSessionVerificationStatusImageView)
val otherSessionNameTextView by bind<TextView>(R.id.otherSessionNameTextView)
val otherSessionDescriptionTextView by bind<TextView>(R.id.otherSessionDescriptionTextView)
}
}

View File

@ -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<DevicesViewState>() {
class OtherSessionsController @Inject constructor(
private val stringProvider: StringProvider,
private val dateFormatter: VectorDateFormatter,
) : TypedEpoxyController<List<DeviceFullInfo>>() {
override fun buildModels(data: DevicesViewState?) {
override fun buildModels(data: List<DeviceFullInfo>?) {
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)
}
}
}
}
}

View File

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

View File

@ -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,
}

View File

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