diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devices/DeviceItem.kt b/vector/src/main/java/im/vector/riotx/features/settings/devices/DeviceItem.kt index 267c0eb676..b792afe666 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/devices/DeviceItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/devices/DeviceItem.kt @@ -53,7 +53,7 @@ abstract class DeviceItem : VectorEpoxyModel() { var detailedMode = false @EpoxyAttribute - var trusted : Boolean? = false + var trusted : Boolean? = null override fun bind(holder: Holder) { holder.root.setOnClickListener { itemClickAction?.invoke() } diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt b/vector/src/main/java/im/vector/riotx/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt index b4eee1fdf7..f2625cbc37 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt @@ -16,7 +16,9 @@ package im.vector.riotx.features.settings.devices import com.airbnb.mvrx.Async +import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success @@ -24,24 +26,56 @@ import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo -import im.vector.riotx.core.di.HasScreenInjector +import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo +import im.vector.matrix.rx.rx import im.vector.riotx.core.platform.EmptyAction import im.vector.riotx.core.platform.EmptyViewEvents import im.vector.riotx.core.platform.VectorViewModel data class DeviceVerificationInfoBottomSheetViewState( - val cryptoDeviceInfo: Async = Uninitialized + val cryptoDeviceInfo: Async = Uninitialized, + val deviceInfo: Async = Uninitialized ) : MvRxState class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@Assisted initialState: DeviceVerificationInfoBottomSheetViewState, + @Assisted val deviceId: String, val session: Session ) : VectorViewModel(initialState) { @AssistedInject.Factory interface Factory { - fun create(initialState: DeviceVerificationInfoBottomSheetViewState): DeviceVerificationInfoBottomSheetViewModel + fun create(initialState: DeviceVerificationInfoBottomSheetViewState, deviceId: String): DeviceVerificationInfoBottomSheetViewModel + } + + init { + session.rx().liveUserCryptoDevices(session.myUserId) + .map { list -> + list.firstOrNull { it.deviceId == deviceId } + } + .execute { + copy( + cryptoDeviceInfo = it + ) + } + setState { + copy(deviceInfo = Loading()) + } + session.getDeviceInfo(deviceId, object : MatrixCallback { + override fun onSuccess(data: DeviceInfo) { + setState { + copy(deviceInfo = Success(data)) + } + } + + override fun onFailure(failure: Throwable) { + setState { + copy(deviceInfo = Fail(failure)) + } + } + }) } companion object : MvRxViewModelFactory { @@ -50,16 +84,8 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As override fun create(viewModelContext: ViewModelContext, state: DeviceVerificationInfoBottomSheetViewState) : DeviceVerificationInfoBottomSheetViewModel? { val fragment: DeviceVerificationInfoBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.deviceVerificationInfoViewModelFactory.create(state) - } - - override fun initialState(viewModelContext: ViewModelContext): DeviceVerificationInfoBottomSheetViewState? { - val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession() val args = viewModelContext.args() - session.getDeviceInfo(args.userId, args.deviceId)?.let { - return DeviceVerificationInfoBottomSheetViewState(cryptoDeviceInfo = Success(it)) - } - return super.initialState(viewModelContext) + return fragment.deviceVerificationInfoViewModelFactory.create(state, args.deviceId) } } diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devices/DeviceVerificationInfoEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/settings/devices/DeviceVerificationInfoEpoxyController.kt index d2e7fff215..decbb15c42 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/devices/DeviceVerificationInfoEpoxyController.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/devices/DeviceVerificationInfoEpoxyController.kt @@ -16,6 +16,7 @@ package im.vector.riotx.features.settings.devices import com.airbnb.epoxy.TypedEpoxyController +import com.airbnb.mvrx.Fail import im.vector.matrix.android.api.session.Session import im.vector.riotx.R import im.vector.riotx.core.epoxy.dividerItem @@ -23,6 +24,7 @@ import im.vector.riotx.core.epoxy.loadingItem import im.vector.riotx.core.resources.ColorProvider import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.ui.list.GenericItem +import im.vector.riotx.core.ui.list.genericFooterItem import im.vector.riotx.core.ui.list.genericItem import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationActionItem import javax.inject.Inject @@ -35,15 +37,11 @@ class DeviceVerificationInfoEpoxyController @Inject constructor(private val stri var callback: Callback? = null override fun buildModels(data: DeviceVerificationInfoBottomSheetViewState?) { - val device = data?.cryptoDeviceInfo?.invoke() - if (device == null) { - loadingItem { - id("loading") - } - } else { - if (device.isVerified) { + val cryptoDeviceInfo = data?.cryptoDeviceInfo?.invoke() + if (cryptoDeviceInfo != null) { + if (cryptoDeviceInfo.isVerified) { genericItem { - id("trust${device.deviceId}") + id("trust${cryptoDeviceInfo.deviceId}") style(GenericItem.STYLE.BIG_TEXT) titleIconResourceId(R.drawable.ic_shield_trusted) title(stringProvider.getString(R.string.encryption_information_verified)) @@ -51,7 +49,7 @@ class DeviceVerificationInfoEpoxyController @Inject constructor(private val stri } } else { genericItem { - id("trust${device.deviceId}") + id("trust${cryptoDeviceInfo.deviceId}") titleIconResourceId(R.drawable.ic_shield_warning) style(GenericItem.STYLE.BIG_TEXT) title(stringProvider.getString(R.string.encryption_information_not_verified)) @@ -60,12 +58,12 @@ class DeviceVerificationInfoEpoxyController @Inject constructor(private val stri } genericItem { - id("info${device.deviceId}") - title(device.displayName() ?: "") - description("(${device.deviceId})") + id("info${cryptoDeviceInfo.deviceId}") + title(cryptoDeviceInfo.displayName() ?: "") + description("(${cryptoDeviceInfo.deviceId})") } - if (!device.isVerified) { + if (!cryptoDeviceInfo.isVerified) { dividerItem { id("d1") } @@ -76,12 +74,12 @@ class DeviceVerificationInfoEpoxyController @Inject constructor(private val stri iconRes(R.drawable.ic_arrow_right) iconColor(colorProvider.getColor(R.color.riotx_accent)) listener { - callback?.onAction(DevicesAction.VerifyMyDevice(device.deviceId)) + callback?.onAction(DevicesAction.VerifyMyDevice(cryptoDeviceInfo.deviceId)) } } } - if (device.deviceId != session.sessionParams.credentials.deviceId) { + if (cryptoDeviceInfo.deviceId != session.sessionParams.credentials.deviceId) { // Add the delete option dividerItem { id("d2") @@ -93,7 +91,7 @@ class DeviceVerificationInfoEpoxyController @Inject constructor(private val stri iconRes(R.drawable.ic_arrow_right) iconColor(colorProvider.getColor(R.color.riotx_destructive_accent)) listener { - callback?.onAction(DevicesAction.Delete(device.deviceId)) + callback?.onAction(DevicesAction.Delete(cryptoDeviceInfo.deviceId)) } } } @@ -108,9 +106,42 @@ class DeviceVerificationInfoEpoxyController @Inject constructor(private val stri iconRes(R.drawable.ic_arrow_right) iconColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary)) listener { - callback?.onAction(DevicesAction.PromptRename(device.deviceId)) + callback?.onAction(DevicesAction.PromptRename(cryptoDeviceInfo.deviceId)) } } + } else if (data?.deviceInfo?.invoke() != null) { + val info = data.deviceInfo.invoke() + genericItem { + id("info${info?.deviceId}") + title(info?.displayName ?: "") + description("(${info?.deviceId})") + } + + genericFooterItem { + id("infoCrypto${info?.deviceId}") + text(stringProvider.getString(R.string.settings_failed_to_get_crypto_device_info)) + } + + if (info?.deviceId != session.sessionParams.credentials.deviceId) { + // Add the delete option + dividerItem { + id("d2") + } + bottomSheetVerificationActionItem { + id("delete") + title(stringProvider.getString(R.string.settings_active_sessions_signout_device)) + titleColor(colorProvider.getColor(R.color.riotx_destructive_accent)) + iconRes(R.drawable.ic_arrow_right) + iconColor(colorProvider.getColor(R.color.riotx_destructive_accent)) + listener { + callback?.onAction(DevicesAction.Delete(info?.deviceId ?: "")) + } + } + } + } else { + loadingItem { + id("loading") + } } } diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesController.kt b/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesController.kt index 769a540a1a..1d275c7da2 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesController.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesController.kt @@ -115,7 +115,7 @@ class DevicesController @Inject constructor(private val errorFormatter: ErrorFor deviceInfo(deviceInfo) currentDevice(isCurrentDevice) itemClickAction { callback?.onDeviceClicked(deviceInfo) } - trusted(cryptoDevices?.firstOrNull { it.deviceId == deviceInfo.deviceId }?.isVerified ?: false) + trusted(cryptoDevices?.firstOrNull { it.deviceId == deviceInfo.deviceId }?.isVerified) } } } diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesViewModel.kt index 22732e31ff..b9a0dc1f44 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/devices/DevicesViewModel.kt @@ -110,7 +110,8 @@ class DevicesViewModel @AssistedInject constructor(@Assisted initialState: Devic if (session.isCryptoEnabled() && !session.sessionParams.credentials.deviceId.isNullOrEmpty()) { setState { copy( - devices = Loading() + // Keep known list if we have it, and let refresh go in backgroung + devices = this.devices.takeIf { it is Success } ?: Loading() ) }