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.databinding.FragmentSettingsDevicesBinding
|
||||||
import im.vector.app.features.crypto.recover.SetupMode
|
import im.vector.app.features.crypto.recover.SetupMode
|
||||||
import im.vector.app.features.crypto.verification.VerificationBottomSheet
|
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.DevicesAction
|
||||||
import im.vector.app.features.settings.devices.DevicesViewEvents
|
import im.vector.app.features.settings.devices.DevicesViewEvents
|
||||||
import im.vector.app.features.settings.devices.DevicesViewModel
|
import im.vector.app.features.settings.devices.DevicesViewModel
|
||||||
import im.vector.app.features.settings.devices.DevicesViewState
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -124,35 +124,53 @@ class VectorSettingsDevicesFragment @Inject constructor() : VectorBaseFragment<F
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) { state ->
|
override fun invalidate() = withState(viewModel) { state ->
|
||||||
val currentDeviceInfo = state.devices()
|
if (state.devices is Success) {
|
||||||
?.firstOrNull {
|
val devices = state.devices()
|
||||||
|
val currentDeviceInfo = devices?.firstOrNull {
|
||||||
it.deviceInfo.deviceId == state.myDeviceId
|
it.deviceInfo.deviceId == state.myDeviceId
|
||||||
}
|
}
|
||||||
|
val otherDevices = devices?.filter { it.deviceInfo.deviceId != state.myDeviceId }
|
||||||
|
|
||||||
if (state.devices is Success && currentDeviceInfo != null) {
|
renderCurrentDevice(currentDeviceInfo)
|
||||||
renderCurrentDevice(state.accountCrossSigningIsTrusted, !state.hasAccountCrossSigning, currentDeviceInfo.deviceInfo.displayName ?: "")
|
renderOtherSessionsView(otherDevices)
|
||||||
} else {
|
} else {
|
||||||
hideCurrentSessionView()
|
hideCurrentSessionView()
|
||||||
|
hideOtherSessionsView()
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRequestStatus(state.request)
|
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() {
|
private fun hideCurrentSessionView() {
|
||||||
views.deviceListHeaderSectionCurrent.isVisible = false
|
views.deviceListHeaderSectionCurrent.isVisible = false
|
||||||
views.deviceListCurrentSession.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>) {
|
private fun handleRequestStatus(unIgnoreRequest: Async<Unit>) {
|
||||||
views.waitingView.root.isVisible = when (unIgnoreRequest) {
|
views.waitingView.root.isVisible = when (unIgnoreRequest) {
|
||||||
is Loading -> true
|
is Loading -> true
|
||||||
|
@ -22,9 +22,9 @@ import androidx.constraintlayout.widget.ConstraintLayout
|
|||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.databinding.ViewCurrentSessionBinding
|
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 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(
|
class CurrentSessionView @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
@ -39,21 +39,14 @@ class CurrentSessionView @JvmOverloads constructor(
|
|||||||
views = ViewCurrentSessionBinding.bind(this)
|
views = ViewCurrentSessionBinding.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun update(accountCrossSigningIsTrusted: Boolean, legacyMode: Boolean, sessionName: String) {
|
fun update(currentDeviceInfo: DeviceFullInfo) {
|
||||||
renderDeviceInfo(sessionName)
|
renderDeviceInfo(currentDeviceInfo.deviceInfo.displayName ?: "")
|
||||||
renderVerificationStatus(accountCrossSigningIsTrusted, legacyMode)
|
renderVerificationStatus(currentDeviceInfo.trustLevelForShield)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderVerificationStatus(accountCrossSigningIsTrusted: Boolean, legacyMode: Boolean) {
|
private fun renderVerificationStatus(trustLevelForShield: RoomEncryptionTrustLevel) {
|
||||||
val deviceTrustLevel = DeviceTrustLevel(crossSigningVerified = accountCrossSigningIsTrusted, locallyVerified = true)
|
views.currentSessionVerificationStatusImageView.render(trustLevelForShield)
|
||||||
val shield = TrustUtils.shieldForTrust(
|
if (trustLevelForShield == RoomEncryptionTrustLevel.Trusted) {
|
||||||
currentDevice = true,
|
|
||||||
trustMSK = accountCrossSigningIsTrusted,
|
|
||||||
legacyMode = legacyMode,
|
|
||||||
deviceTrustLevel = deviceTrustLevel
|
|
||||||
)
|
|
||||||
views.currentSessionVerificationStatusImageView.render(shield)
|
|
||||||
if (deviceTrustLevel.crossSigningVerified) {
|
|
||||||
renderCrossSigningVerified()
|
renderCrossSigningVerified()
|
||||||
} else {
|
} else {
|
||||||
renderCrossSigningUnverified()
|
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
|
package im.vector.app.features.settings.devices.v2.list
|
||||||
|
|
||||||
import com.airbnb.epoxy.TypedEpoxyController
|
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
|
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.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.extensions.configureWith
|
||||||
import im.vector.app.databinding.ViewOtherSessionsBinding
|
import im.vector.app.databinding.ViewOtherSessionsBinding
|
||||||
import im.vector.app.features.settings.devices.DeviceFullInfo
|
import im.vector.app.features.settings.devices.DeviceFullInfo
|
||||||
import timber.log.Timber
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
class OtherSessionsView @JvmOverloads constructor(
|
class OtherSessionsView @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
attrs: AttributeSet? = null,
|
attrs: AttributeSet? = null,
|
||||||
defStyleAttr: Int = 0
|
defStyleAttr: Int = 0
|
||||||
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
@Inject lateinit var otherSessionsController: OtherSessionsController
|
||||||
|
|
||||||
private val views: ViewOtherSessionsBinding
|
private val views: ViewOtherSessionsBinding
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -38,6 +43,8 @@ class OtherSessionsView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun update(devices: List<DeviceFullInfo>) {
|
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…
x
Reference in New Issue
Block a user