diff --git a/CHANGES.md b/CHANGES.md index 86b796869e..e0031644a6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,7 @@ Features ✨: Improvements 🙌: - You can now join room through permalink and within room directory search - Add long click gesture to copy userId, user display name, room name, room topic and room alias (#1774) + - Do not propose to verify session if there is only one session and 4S is not configured (#1901) Bugfix 🐛: - Display name not shown under Settings/General (#1926) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt index 6a334898c0..c8b12e26a5 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheet.kt @@ -39,7 +39,7 @@ data class DeviceVerificationInfoArgs( val deviceId: String ) : Parcelable -class DeviceVerificationInfoBottomSheet : VectorBaseBottomSheetDialogFragment(), DeviceVerificationInfoEpoxyController.Callback { +class DeviceVerificationInfoBottomSheet : VectorBaseBottomSheetDialogFragment(), DeviceVerificationInfoBottomSheetController.Callback { private val viewModel: DeviceVerificationInfoBottomSheetViewModel by fragmentViewModel(DeviceVerificationInfoBottomSheetViewModel::class) @@ -54,17 +54,17 @@ class DeviceVerificationInfoBottomSheet : VectorBaseBottomSheetDialogFragment(), injector.inject(this) } - @Inject lateinit var epoxyController: DeviceVerificationInfoEpoxyController + @Inject lateinit var controller: DeviceVerificationInfoBottomSheetController override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) recyclerView.configureWith( - epoxyController, + controller, showDivider = false, hasFixedSize = false) - epoxyController.callback = this + controller.callback = this bottomSheetTitle.isVisible = false } @@ -74,7 +74,7 @@ class DeviceVerificationInfoBottomSheet : VectorBaseBottomSheetDialogFragment(), } override fun invalidate() = withState(viewModel) { - epoxyController.setData(it) + controller.setData(it) super.invalidate() } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoEpoxyController.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt similarity index 89% rename from vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoEpoxyController.kt rename to vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt index bae31124ed..25a98bb4b5 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt @@ -16,9 +16,6 @@ package im.vector.app.features.settings.devices import com.airbnb.epoxy.TypedEpoxyController -import org.matrix.android.sdk.api.extensions.orFalse -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import im.vector.app.R import im.vector.app.core.epoxy.dividerItem import im.vector.app.core.epoxy.loadingItem @@ -28,12 +25,14 @@ import im.vector.app.core.ui.list.GenericItem import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.core.ui.list.genericItem import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem +import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import timber.log.Timber import javax.inject.Inject -class DeviceVerificationInfoEpoxyController @Inject constructor(private val stringProvider: StringProvider, - private val colorProvider: ColorProvider, - private val session: Session) +class DeviceVerificationInfoBottomSheetController @Inject constructor( + private val stringProvider: StringProvider, + private val colorProvider: ColorProvider) : TypedEpoxyController() { var callback: Callback? = null @@ -67,16 +66,18 @@ class DeviceVerificationInfoEpoxyController @Inject constructor(private val stri if (data.hasAccountCrossSigning) { // Cross Signing is enabled - handleE2EWithCrossSigning(data.isMine, data.accountCrossSigningIsTrusted, cryptoDeviceInfo, shield) + handleE2EWithCrossSigning(data, cryptoDeviceInfo, shield) } else { - handleE2EInLegacy(data.isMine, cryptoDeviceInfo, shield) + handleE2EInLegacy(data, cryptoDeviceInfo, shield) } // COMMON ACTIONS (Rename / signout) addGenericDeviceManageActions(data, cryptoDeviceInfo.deviceId) } - private fun handleE2EWithCrossSigning(isMine: Boolean, currentSessionIsTrusted: Boolean, cryptoDeviceInfo: CryptoDeviceInfo, shield: Int) { + private fun handleE2EWithCrossSigning(data: DeviceVerificationInfoBottomSheetViewState, cryptoDeviceInfo: CryptoDeviceInfo, shield: Int) { + val isMine = data.isMine + val currentSessionIsTrusted = data.accountCrossSigningIsTrusted Timber.v("handleE2EWithCrossSigning $isMine, $cryptoDeviceInfo, $shield") if (isMine) { @@ -88,14 +89,18 @@ class DeviceVerificationInfoEpoxyController @Inject constructor(private val stri title(stringProvider.getString(R.string.encryption_information_verified)) description(stringProvider.getString(R.string.settings_active_sessions_verified_device_desc)) } - } else { - // You need to complete security + } else if (data.canVerifySession) { + // You need to complete security, only if there are other session(s) available, or if 4S contains secrets genericItem { id("trust${cryptoDeviceInfo.deviceId}") style(GenericItem.STYLE.BIG_TEXT) titleIconResourceId(shield) title(stringProvider.getString(R.string.crosssigning_verify_this_session)) - description(stringProvider.getString(R.string.confirm_your_identity)) + if (data.hasOtherSessions) { + description(stringProvider.getString(R.string.confirm_your_identity)) + } else { + description(stringProvider.getString(R.string.confirm_your_identity_quad_s)) + } } } } else { @@ -132,7 +137,7 @@ class DeviceVerificationInfoEpoxyController @Inject constructor(private val stri description("(${cryptoDeviceInfo.deviceId})") } - if (isMine && !currentSessionIsTrusted) { + if (isMine && !currentSessionIsTrusted && data.canVerifySession) { // Add complete security dividerItem { id("completeSecurityDiv") @@ -158,8 +163,9 @@ class DeviceVerificationInfoEpoxyController @Inject constructor(private val stri } } - private fun handleE2EInLegacy(isMine: Boolean, cryptoDeviceInfo: CryptoDeviceInfo, shield: Int) { + private fun handleE2EInLegacy(data: DeviceVerificationInfoBottomSheetViewState, cryptoDeviceInfo: CryptoDeviceInfo, shield: Int) { // ==== Legacy + val isMine = data.isMine // TRUST INFO SECTION if (cryptoDeviceInfo.trustLevel?.isLocallyVerified() == true) { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt index 6f5bd909c7..ad840d2efb 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt @@ -15,31 +15,19 @@ */ package im.vector.app.features.settings.devices -import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.MvRxViewModelFactory -import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo -import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import org.matrix.android.sdk.rx.rx -data class DeviceVerificationInfoBottomSheetViewState( - val cryptoDeviceInfo: Async = Uninitialized, - val deviceInfo: Async = Uninitialized, - val hasAccountCrossSigning: Boolean = false, - val accountCrossSigningIsTrusted: Boolean = false, - val isMine: Boolean = false -) : MvRxState - class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@Assisted initialState: DeviceVerificationInfoBottomSheetViewState, @Assisted val deviceId: String, val session: Session @@ -55,7 +43,8 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As setState { copy( hasAccountCrossSigning = session.cryptoService().crossSigningService().isCrossSigningInitialized(), - accountCrossSigningIsTrusted = session.cryptoService().crossSigningService().isCrossSigningVerified() + accountCrossSigningIsTrusted = session.cryptoService().crossSigningService().isCrossSigningVerified(), + isRecoverySetup = session.sharedSecretStorageService.isRecoverySetup() ) } session.rx().liveCrossSigningInfo(session.myUserId) @@ -77,6 +66,14 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As ) } + session.rx().liveUserCryptoDevices(session.myUserId) + .map { it.size } + .execute { + copy( + hasOtherSessions = it.invoke() ?: 0 > 1 + ) + } + setState { copy(deviceInfo = Loading()) } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewState.kt new file mode 100644 index 0000000000..a736b0442c --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewState.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 + +import com.airbnb.mvrx.Async +import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.Uninitialized +import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo +import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo + +data class DeviceVerificationInfoBottomSheetViewState( + val cryptoDeviceInfo: Async = Uninitialized, + val deviceInfo: Async = Uninitialized, + val hasAccountCrossSigning: Boolean = false, + val accountCrossSigningIsTrusted: Boolean = false, + val isMine: Boolean = false, + val hasOtherSessions: Boolean = false, + val isRecoverySetup: Boolean = false +) : MvRxState { + + val canVerifySession: Boolean + get() = hasOtherSessions || isRecoverySetup +} diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 7506a4d502..2e031cfd4f 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2413,6 +2413,7 @@ Verify login Interactively Verify by Emoji Confirm your identity by verifying this login from one of your other sessions, granting it access to encrypted messages. + Confirm your identity by verifying this login, granting it access to encrypted messages. Mark as Trusted Please choose a username.