Render other sessions.
This commit is contained in:
parent
eb86a4f33c
commit
eaf7da8e6e
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue