mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-22 17:35:54 +03:00
New sign in detection flow
This commit is contained in:
parent
e36367c040
commit
49e5fafb2d
23 changed files with 651 additions and 27 deletions
|
@ -239,7 +239,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
override fun getDevicesList(callback: MatrixCallback<DevicesListResponse>) {
|
||||
getDevicesTask
|
||||
.configureWith {
|
||||
this.executionThread = TaskThread.CRYPTO
|
||||
// this.executionThread = TaskThread.CRYPTO
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
|
@ -729,30 +729,30 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
*/
|
||||
private fun onRoomKeyEvent(event: Event) {
|
||||
val roomKeyContent = event.getClearContent().toModel<RoomKeyContent>() ?: return
|
||||
Timber.v("## onRoomKeyEvent() : type<${event.type}> , sessionId<${roomKeyContent.sessionId}>")
|
||||
Timber.v("## GOSSIP onRoomKeyEvent() : type<${event.type}> , sessionId<${roomKeyContent.sessionId}>")
|
||||
if (roomKeyContent.roomId.isNullOrEmpty() || roomKeyContent.algorithm.isNullOrEmpty()) {
|
||||
Timber.e("## onRoomKeyEvent() : missing fields")
|
||||
Timber.e("## GOSSIP onRoomKeyEvent() : missing fields")
|
||||
return
|
||||
}
|
||||
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(roomKeyContent.roomId, roomKeyContent.algorithm)
|
||||
if (alg == null) {
|
||||
Timber.e("## onRoomKeyEvent() : Unable to handle keys for ${roomKeyContent.algorithm}")
|
||||
Timber.e("## GOSSIP onRoomKeyEvent() : Unable to handle keys for ${roomKeyContent.algorithm}")
|
||||
return
|
||||
}
|
||||
alg.onRoomKeyEvent(event, keysBackupService)
|
||||
}
|
||||
|
||||
private fun onSecretSendReceived(event: Event) {
|
||||
Timber.i("## onSecretSend() : onSecretSendReceived ${event.content?.get("sender_key")}")
|
||||
Timber.i("## GOSSIP onSecretSend() : onSecretSendReceived ${event.content?.get("sender_key")}")
|
||||
if (!event.isEncrypted()) {
|
||||
// secret send messages must be encrypted
|
||||
Timber.e("## onSecretSend() :Received unencrypted secret send event")
|
||||
Timber.e("## GOSSIP onSecretSend() :Received unencrypted secret send event")
|
||||
return
|
||||
}
|
||||
|
||||
// Was that sent by us?
|
||||
if (event.senderId != credentials.userId) {
|
||||
Timber.e("## onSecretSend() : Ignore secret from other user ${event.senderId}")
|
||||
Timber.e("## GOSSIP onSecretSend() : Ignore secret from other user ${event.senderId}")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -762,7 +762,7 @@ internal class DefaultCryptoService @Inject constructor(
|
|||
.getOutgoingSecretKeyRequests().firstOrNull { it.requestId == secretContent.requestId }
|
||||
|
||||
if (existingRequest == null) {
|
||||
Timber.i("## onSecretSend() : Ignore secret that was not requested: ${secretContent.requestId}")
|
||||
Timber.i("## GOSSIP onSecretSend() : Ignore secret that was not requested: ${secretContent.requestId}")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -757,6 +757,7 @@ internal class DefaultVerificationService @Inject constructor(
|
|||
|
||||
private suspend fun onReadyReceived(event: Event) {
|
||||
val readyReq = event.getClearContent().toModel<KeyVerificationReady>()?.asValidObject()
|
||||
Timber.v("## SAS onReadyReceived $readyReq")
|
||||
|
||||
if (readyReq == null || event.senderId == null) {
|
||||
// ignore
|
||||
|
|
|
@ -26,6 +26,8 @@ import im.vector.riotx.features.attachments.preview.AttachmentsPreviewFragment
|
|||
import im.vector.riotx.features.createdirect.CreateDirectRoomDirectoryUsersFragment
|
||||
import im.vector.riotx.features.createdirect.CreateDirectRoomKnownUsersFragment
|
||||
import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsFragment
|
||||
import im.vector.riotx.features.crypto.verification.cancel.VerificationCancelFragment
|
||||
import im.vector.riotx.features.crypto.verification.cancel.VerificationNotMeFragment
|
||||
import im.vector.riotx.features.crypto.verification.choose.VerificationChooseMethodFragment
|
||||
import im.vector.riotx.features.crypto.verification.conclusion.VerificationConclusionFragment
|
||||
import im.vector.riotx.features.crypto.verification.emoji.VerificationEmojiCodeFragment
|
||||
|
@ -336,6 +338,16 @@ interface FragmentModule {
|
|||
@FragmentKey(VerificationConclusionFragment::class)
|
||||
fun bindVerificationConclusionFragment(fragment: VerificationConclusionFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(VerificationCancelFragment::class)
|
||||
fun bindVerificationCancelFragment(fragment: VerificationCancelFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(VerificationNotMeFragment::class)
|
||||
fun bindVerificationNotMeFragment(fragment: VerificationNotMeFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(QrCodeScannerFragment::class)
|
||||
|
|
|
@ -16,9 +16,11 @@
|
|||
package im.vector.riotx.features.crypto.verification
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
|
@ -39,14 +41,18 @@ import im.vector.riotx.R
|
|||
import im.vector.riotx.core.di.ScreenComponent
|
||||
import im.vector.riotx.core.extensions.commitTransaction
|
||||
import im.vector.riotx.core.extensions.exhaustive
|
||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
|
||||
import im.vector.riotx.features.crypto.quads.SharedSecureStorageActivity
|
||||
import im.vector.riotx.features.crypto.verification.cancel.VerificationCancelFragment
|
||||
import im.vector.riotx.features.crypto.verification.cancel.VerificationNotMeFragment
|
||||
import im.vector.riotx.features.crypto.verification.choose.VerificationChooseMethodFragment
|
||||
import im.vector.riotx.features.crypto.verification.conclusion.VerificationConclusionFragment
|
||||
import im.vector.riotx.features.crypto.verification.emoji.VerificationEmojiCodeFragment
|
||||
import im.vector.riotx.features.crypto.verification.qrconfirmation.VerificationQrScannedByOtherFragment
|
||||
import im.vector.riotx.features.crypto.verification.request.VerificationRequestFragment
|
||||
import im.vector.riotx.features.home.AvatarRenderer
|
||||
import im.vector.riotx.features.settings.VectorSettingsActivity
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
@ -58,6 +64,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
data class VerificationArgs(
|
||||
val otherUserId: String,
|
||||
val verificationId: String? = null,
|
||||
val verificationLocalId: String? = null,
|
||||
val roomId: String? = null,
|
||||
// Special mode where UX should show loading wheel until other session sends a request/tx
|
||||
val selfVerificationMode: Boolean = false
|
||||
|
@ -80,13 +87,17 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
lateinit var otherUserNameText: TextView
|
||||
|
||||
@BindView(R.id.verificationRequestShield)
|
||||
lateinit var otherUserShield: View
|
||||
lateinit var otherUserShield: ImageView
|
||||
|
||||
@BindView(R.id.verificationRequestAvatar)
|
||||
lateinit var otherUserAvatarImageView: ImageView
|
||||
|
||||
override fun getLayoutResId() = R.layout.bottom_sheet_verification
|
||||
|
||||
init {
|
||||
isCancelable = false
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
|
@ -110,10 +121,27 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
.show()
|
||||
Unit
|
||||
}
|
||||
VerificationBottomSheetViewEvents.GoToSettings -> {
|
||||
dismiss()
|
||||
(activity as? VectorBaseActivity)?.navigator?.openSettings(requireContext(), VectorSettingsActivity.EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY)
|
||||
}
|
||||
}.exhaustive
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return super.onCreateDialog(savedInstanceState).apply {
|
||||
setOnKeyListener { _, keyCode, keyEvent ->
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && keyEvent.action == KeyEvent.ACTION_UP) {
|
||||
viewModel.queryCancel()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (resultCode == Activity.RESULT_OK && requestCode == SECRET_REQUEST_CODE) {
|
||||
data?.getStringExtra(SharedSecureStorageActivity.EXTRA_DATA_RESULT)?.let {
|
||||
|
@ -127,15 +155,17 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
|
||||
state.otherUserMxItem?.let { matrixItem ->
|
||||
if (state.isMe) {
|
||||
|
||||
avatarRenderer.render(matrixItem, otherUserAvatarImageView)
|
||||
if (state.sasTransactionState == VerificationTxState.Verified
|
||||
|| state.qrTransactionState == VerificationTxState.Verified
|
||||
|| state.verifiedFromPrivateKeys) {
|
||||
otherUserAvatarImageView.setImageResource(R.drawable.ic_shield_trusted)
|
||||
otherUserShield.setImageResource(R.drawable.ic_shield_trusted)
|
||||
} else {
|
||||
otherUserAvatarImageView.setImageResource(R.drawable.ic_shield_warning)
|
||||
otherUserShield.setImageResource(R.drawable.ic_shield_warning)
|
||||
}
|
||||
otherUserNameText.text = getString(R.string.complete_security)
|
||||
otherUserShield.isVisible = false
|
||||
otherUserShield.isVisible = true
|
||||
} else {
|
||||
avatarRenderer.render(matrixItem, otherUserAvatarImageView)
|
||||
|
||||
|
@ -149,6 +179,18 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
if (state.userThinkItsNotHim) {
|
||||
otherUserNameText.text = getString(R.string.dialog_title_warning)
|
||||
showFragment(VerificationNotMeFragment::class, Bundle())
|
||||
return@withState
|
||||
}
|
||||
|
||||
if (state.userWantsToCancel) {
|
||||
otherUserNameText.text = getString(R.string.are_you_sure)
|
||||
showFragment(VerificationCancelFragment::class, Bundle())
|
||||
return@withState
|
||||
}
|
||||
|
||||
if (state.selfVerificationMode && state.verifiedFromPrivateKeys) {
|
||||
showFragment(VerificationConclusionFragment::class, Bundle().apply {
|
||||
putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(true, null, state.isMe))
|
||||
|
|
|
@ -24,5 +24,6 @@ import im.vector.riotx.core.platform.VectorViewEvents
|
|||
sealed class VerificationBottomSheetViewEvents : VectorViewEvents {
|
||||
object Dismiss : VerificationBottomSheetViewEvents()
|
||||
object AccessSecretStore : VerificationBottomSheetViewEvents()
|
||||
object GoToSettings : VerificationBottomSheetViewEvents()
|
||||
data class ModalError(val errorMessage: CharSequence) : VerificationBottomSheetViewEvents()
|
||||
}
|
||||
|
|
|
@ -31,7 +31,9 @@ import im.vector.matrix.android.api.session.Session
|
|||
import im.vector.matrix.android.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
|
||||
import im.vector.matrix.android.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
|
||||
import im.vector.matrix.android.api.session.crypto.verification.CancelCode
|
||||
import im.vector.matrix.android.api.session.crypto.verification.IncomingSasVerificationTransaction
|
||||
import im.vector.matrix.android.api.session.crypto.verification.PendingVerificationRequest
|
||||
import im.vector.matrix.android.api.session.crypto.verification.QrCodeVerificationTransaction
|
||||
import im.vector.matrix.android.api.session.crypto.verification.SasVerificationTransaction
|
||||
import im.vector.matrix.android.api.session.crypto.verification.VerificationMethod
|
||||
|
@ -44,7 +46,6 @@ import im.vector.matrix.android.api.util.MatrixItem
|
|||
import im.vector.matrix.android.api.util.toMatrixItem
|
||||
import im.vector.matrix.android.internal.crypto.crosssigning.fromBase64
|
||||
import im.vector.matrix.android.internal.crypto.crosssigning.isVerified
|
||||
import im.vector.matrix.android.api.session.crypto.verification.PendingVerificationRequest
|
||||
import im.vector.riotx.core.extensions.exhaustive
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
import timber.log.Timber
|
||||
|
@ -60,7 +61,10 @@ data class VerificationBottomSheetViewState(
|
|||
// true when we display the loading and we wait for the other (incoming request)
|
||||
val selfVerificationMode: Boolean = false,
|
||||
val verifiedFromPrivateKeys: Boolean = false,
|
||||
val isMe: Boolean = false
|
||||
val isMe: Boolean = false,
|
||||
val currentDeviceCanCrossSign: Boolean = false,
|
||||
val userWantsToCancel: Boolean = false,
|
||||
val userThinkItsNotHim: Boolean = false
|
||||
) : MvRxState
|
||||
|
||||
class VerificationBottomSheetViewModel @AssistedInject constructor(
|
||||
|
@ -111,7 +115,8 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
|
|||
pendingRequest = if (pr != null) Success(pr) else Uninitialized,
|
||||
selfVerificationMode = selfVerificationMode,
|
||||
roomId = args.roomId,
|
||||
isMe = args.otherUserId == session.myUserId
|
||||
isMe = args.otherUserId == session.myUserId,
|
||||
currentDeviceCanCrossSign = session.cryptoService().crossSigningService().canCrossSign()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -137,6 +142,46 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
|
|||
args: VerificationBottomSheet.VerificationArgs): VerificationBottomSheetViewModel
|
||||
}
|
||||
|
||||
fun queryCancel() {
|
||||
setState {
|
||||
copy(userWantsToCancel = true)
|
||||
}
|
||||
}
|
||||
|
||||
fun confirmCancel() = withState { state ->
|
||||
session.cryptoService()
|
||||
.verificationService()
|
||||
.getExistingTransaction(state.otherUserMxItem?.id ?: "", state.transactionId ?: "")
|
||||
?.cancel(CancelCode.User)
|
||||
_viewEvents.post(VerificationBottomSheetViewEvents.Dismiss)
|
||||
}
|
||||
|
||||
fun continueFromCancel() {
|
||||
setState {
|
||||
copy(userWantsToCancel = false)
|
||||
}
|
||||
}
|
||||
|
||||
fun continueFromWasNotMe() {
|
||||
setState {
|
||||
copy(userThinkItsNotHim = false)
|
||||
}
|
||||
}
|
||||
|
||||
fun itWasNotMe() {
|
||||
setState {
|
||||
copy(userThinkItsNotHim = true)
|
||||
}
|
||||
}
|
||||
|
||||
fun goToSettings() = withState { state ->
|
||||
session.cryptoService()
|
||||
.verificationService()
|
||||
.getExistingTransaction(state.otherUserMxItem?.id ?: "", state.transactionId ?: "")
|
||||
?.cancel(CancelCode.User)
|
||||
_viewEvents.post(VerificationBottomSheetViewEvents.GoToSettings)
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<VerificationBottomSheetViewModel, VerificationBottomSheetViewState> {
|
||||
|
||||
override fun create(viewModelContext: ViewModelContext, state: VerificationBottomSheetViewState): VerificationBottomSheetViewModel? {
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification.cancel
|
||||
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.dividerItem
|
||||
import im.vector.riotx.core.resources.ColorProvider
|
||||
import im.vector.riotx.core.resources.StringProvider
|
||||
import im.vector.riotx.features.crypto.verification.VerificationBottomSheetViewState
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerificationCancelController @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val colorProvider: ColorProvider
|
||||
) : EpoxyController() {
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
private var viewState: VerificationBottomSheetViewState? = null
|
||||
|
||||
fun update(viewState: VerificationBottomSheetViewState) {
|
||||
this.viewState = viewState
|
||||
requestModelBuild()
|
||||
}
|
||||
|
||||
override fun buildModels() {
|
||||
val state = viewState ?: return
|
||||
|
||||
if (state.isMe) {
|
||||
if (state.currentDeviceCanCrossSign) {
|
||||
bottomSheetVerificationNoticeItem {
|
||||
id("notice")
|
||||
notice(stringProvider.getString(R.string.verify_cancel_self_verification_from_trusted))
|
||||
}
|
||||
} else {
|
||||
bottomSheetVerificationNoticeItem {
|
||||
id("notice")
|
||||
notice(stringProvider.getString(R.string.verify_cancel_self_verification_from_untrusted))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bottomSheetVerificationNoticeItem {
|
||||
id("notice")
|
||||
notice(stringProvider.getString(R.string.verify_cancel_self_verification_from_untrusted))
|
||||
}
|
||||
}
|
||||
|
||||
dividerItem {
|
||||
id("sep0")
|
||||
}
|
||||
|
||||
bottomSheetVerificationActionItem {
|
||||
id("cancel")
|
||||
title(stringProvider.getString(R.string.cancel))
|
||||
titleColor(colorProvider.getColor(R.color.riotx_destructive_accent))
|
||||
iconRes(R.drawable.ic_arrow_right)
|
||||
iconColor(colorProvider.getColor(R.color.riotx_destructive_accent))
|
||||
listener { listener?.onTapCancel() }
|
||||
}
|
||||
|
||||
dividerItem {
|
||||
id("sep1")
|
||||
}
|
||||
|
||||
bottomSheetVerificationActionItem {
|
||||
id("continue")
|
||||
title(stringProvider.getString(R.string._continue))
|
||||
titleColor(colorProvider.getColor(R.color.riotx_positive_accent))
|
||||
iconRes(R.drawable.ic_arrow_right)
|
||||
iconColor(colorProvider.getColor(R.color.riotx_positive_accent))
|
||||
listener { listener?.onTapContinue() }
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onTapCancel()
|
||||
fun onTapContinue()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification.cancel
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.airbnb.mvrx.parentFragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.extensions.cleanup
|
||||
import im.vector.riotx.core.extensions.configureWith
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.features.crypto.verification.VerificationBottomSheetViewModel
|
||||
import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerificationCancelFragment @Inject constructor(
|
||||
val controller: VerificationCancelController
|
||||
) : VectorBaseFragment(), VerificationCancelController.Listener {
|
||||
|
||||
private val viewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
|
||||
|
||||
override fun getLayoutResId() = R.layout.bottom_sheet_verification_child_fragment
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setupRecyclerView()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
bottomSheetVerificationRecyclerView.cleanup()
|
||||
controller.listener = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
|
||||
controller.listener = this
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
controller.update(state)
|
||||
}
|
||||
|
||||
override fun onTapCancel() {
|
||||
viewModel.confirmCancel()
|
||||
}
|
||||
|
||||
override fun onTapContinue() {
|
||||
viewModel.continueFromCancel()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification.cancel
|
||||
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.dividerItem
|
||||
import im.vector.riotx.core.resources.ColorProvider
|
||||
import im.vector.riotx.core.resources.StringProvider
|
||||
import im.vector.riotx.features.crypto.verification.VerificationBottomSheetViewState
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
|
||||
import im.vector.riotx.features.html.EventHtmlRenderer
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerificationNotMeController @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val colorProvider: ColorProvider,
|
||||
private val eventHtmlRenderer: EventHtmlRenderer
|
||||
) : EpoxyController() {
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
private var viewState: VerificationBottomSheetViewState? = null
|
||||
|
||||
fun update(viewState: VerificationBottomSheetViewState) {
|
||||
this.viewState = viewState
|
||||
requestModelBuild()
|
||||
}
|
||||
|
||||
override fun buildModels() {
|
||||
|
||||
bottomSheetVerificationNoticeItem {
|
||||
id("notice")
|
||||
notice(eventHtmlRenderer.render(stringProvider.getString(R.string.verify_not_me_self_verification)))
|
||||
}
|
||||
|
||||
dividerItem {
|
||||
id("sep0")
|
||||
}
|
||||
|
||||
bottomSheetVerificationActionItem {
|
||||
id("skip")
|
||||
title(stringProvider.getString(R.string.skip))
|
||||
titleColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
|
||||
iconRes(R.drawable.ic_arrow_right)
|
||||
iconColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
|
||||
listener { listener?.onTapSkip() }
|
||||
}
|
||||
|
||||
dividerItem {
|
||||
id("sep1")
|
||||
}
|
||||
|
||||
bottomSheetVerificationActionItem {
|
||||
id("settings")
|
||||
title(stringProvider.getString(R.string.settings))
|
||||
titleColor(colorProvider.getColor(R.color.riotx_positive_accent))
|
||||
iconRes(R.drawable.ic_arrow_right)
|
||||
iconColor(colorProvider.getColor(R.color.riotx_positive_accent))
|
||||
listener { listener?.onTapSettings() }
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onTapSkip()
|
||||
fun onTapSettings()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.riotx.features.crypto.verification.cancel
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.airbnb.mvrx.parentFragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.extensions.cleanup
|
||||
import im.vector.riotx.core.extensions.configureWith
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.features.crypto.verification.VerificationBottomSheetViewModel
|
||||
import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerificationNotMeFragment @Inject constructor(
|
||||
val controller: VerificationNotMeController
|
||||
) : VectorBaseFragment(), VerificationNotMeController.Listener {
|
||||
|
||||
private val viewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
|
||||
|
||||
override fun getLayoutResId() = R.layout.bottom_sheet_verification_child_fragment
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setupRecyclerView()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
bottomSheetVerificationRecyclerView.cleanup()
|
||||
controller.listener = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
|
||||
controller.listener = this
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
controller.update(state)
|
||||
}
|
||||
|
||||
override fun onTapSkip() {
|
||||
viewModel.continueFromWasNotMe()
|
||||
}
|
||||
|
||||
override fun onTapSettings() {
|
||||
viewModel.goToSettings()
|
||||
}
|
||||
}
|
|
@ -95,10 +95,27 @@ class VerificationChooseMethodController @Inject constructor(
|
|||
listener { listener?.doVerifyBySas() }
|
||||
}
|
||||
}
|
||||
|
||||
if (state.isMe && state.canCrossSign) {
|
||||
dividerItem {
|
||||
id("sep_notMe")
|
||||
}
|
||||
|
||||
bottomSheetVerificationActionItem {
|
||||
id("wasnote")
|
||||
title(stringProvider.getString(R.string.verify_new_session_was_not_me))
|
||||
titleColor(colorProvider.getColor(R.color.riotx_destructive_accent))
|
||||
subTitle(stringProvider.getString(R.string.verify_new_session_compromized))
|
||||
iconRes(R.drawable.ic_arrow_right)
|
||||
iconColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
|
||||
listener { listener?.onClickOnWasNotMe() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun openCamera()
|
||||
fun doVerifyBySas()
|
||||
fun onClickOnWasNotMe()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,6 +89,10 @@ class VerificationChooseMethodFragment @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onClickOnWasNotMe() {
|
||||
sharedViewModel.itWasNotMe()
|
||||
}
|
||||
|
||||
private fun doOpenQRCodeScanner() {
|
||||
QrCodeScannerActivity.startForResult(this)
|
||||
}
|
||||
|
|
|
@ -39,7 +39,9 @@ data class VerificationChooseMethodViewState(
|
|||
val otherCanShowQrCode: Boolean = false,
|
||||
val otherCanScanQrCode: Boolean = false,
|
||||
val qrCodeText: String? = null,
|
||||
val SASModeAvailable: Boolean = false
|
||||
val SASModeAvailable: Boolean = false,
|
||||
val isMe: Boolean = false,
|
||||
val canCrossSign: Boolean = false
|
||||
) : MvRxState
|
||||
|
||||
class VerificationChooseMethodViewModel @AssistedInject constructor(
|
||||
|
@ -61,6 +63,10 @@ class VerificationChooseMethodViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun verificationRequestCreated(pr: PendingVerificationRequest) {
|
||||
verificationRequestUpdated(pr)
|
||||
}
|
||||
|
||||
override fun verificationRequestUpdated(pr: PendingVerificationRequest) = withState { state ->
|
||||
val pvr = session.cryptoService().verificationService().getExistingVerificationRequest(state.otherUserId, state.transactionId)
|
||||
|
||||
|
@ -103,6 +109,8 @@ class VerificationChooseMethodViewModel @AssistedInject constructor(
|
|||
val qrCodeVerificationTransaction = verificationService.getExistingTransaction(args.otherUserId, args.verificationId ?: "")
|
||||
|
||||
return VerificationChooseMethodViewState(otherUserId = args.otherUserId,
|
||||
isMe = session.myUserId == pvr?.otherUserId,
|
||||
canCrossSign = session.cryptoService().crossSigningService().canCrossSign(),
|
||||
transactionId = args.verificationId ?: "",
|
||||
otherCanShowQrCode = pvr?.otherCanShowQrCode().orFalse(),
|
||||
otherCanScanQrCode = pvr?.otherCanScanQrCode().orFalse(),
|
||||
|
|
|
@ -84,11 +84,17 @@ class VerificationRequestController @Inject constructor(
|
|||
listener { listener?.onClickDismiss() }
|
||||
}
|
||||
} else {
|
||||
val styledText = matrixItem.let {
|
||||
stringProvider.getString(R.string.verification_request_notice, it.id)
|
||||
.toSpannable()
|
||||
.colorizeMatchingText(it.id, colorProvider.getColorFromAttribute(R.attr.vctr_notice_text_color))
|
||||
}
|
||||
val styledText =
|
||||
if (state.isMe) {
|
||||
stringProvider.getString(R.string.verify_new_session_notice)
|
||||
} else {
|
||||
matrixItem.let {
|
||||
stringProvider.getString(R.string.verification_request_notice, it.id)
|
||||
.toSpannable()
|
||||
.colorizeMatchingText(it.id, colorProvider.getColorFromAttribute(R.attr.vctr_notice_text_color))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bottomSheetVerificationNoticeItem {
|
||||
id("notice")
|
||||
|
@ -119,18 +125,43 @@ class VerificationRequestController @Inject constructor(
|
|||
}
|
||||
is Success -> {
|
||||
if (!pr.invoke().isReady) {
|
||||
bottomSheetVerificationWaitingItem {
|
||||
id("waiting")
|
||||
title(stringProvider.getString(R.string.verification_request_waiting_for, matrixItem.getBestName()))
|
||||
if (state.isMe) {
|
||||
bottomSheetVerificationWaitingItem {
|
||||
id("waiting")
|
||||
title(stringProvider.getString(R.string.verification_request_waiting))
|
||||
}
|
||||
} else {
|
||||
bottomSheetVerificationWaitingItem {
|
||||
id("waiting")
|
||||
title(stringProvider.getString(R.string.verification_request_waiting_for, matrixItem.getBestName()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state.isMe && state.currentDeviceCanCrossSign) {
|
||||
|
||||
dividerItem {
|
||||
id("sep_notMe")
|
||||
}
|
||||
|
||||
bottomSheetVerificationActionItem {
|
||||
id("wasnote")
|
||||
title(stringProvider.getString(R.string.verify_new_session_was_not_me))
|
||||
titleColor(colorProvider.getColor(R.color.riotx_destructive_accent))
|
||||
subTitle(stringProvider.getString(R.string.verify_new_session_compromized))
|
||||
iconRes(R.drawable.ic_arrow_right)
|
||||
iconColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
|
||||
listener { listener?.onClickOnWasNotMe() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onClickOnVerificationStart()
|
||||
fun onClickOnWasNotMe()
|
||||
fun onClickRecoverFromPassphrase()
|
||||
fun onClickDismiss()
|
||||
}
|
||||
|
|
|
@ -69,4 +69,8 @@ class VerificationRequestFragment @Inject constructor(
|
|||
override fun onClickDismiss() {
|
||||
viewModel.handle(VerificationAction.SkipVerification)
|
||||
}
|
||||
|
||||
override fun onClickOnWasNotMe() {
|
||||
viewModel.itWasNotMe()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
/*
|
||||
* Copyright 2019 New Vector Ltd
|
||||
*
|
||||
|
@ -19,8 +20,10 @@ package im.vector.riotx.features.home
|
|||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.forEachIndexed
|
||||
import androidx.lifecycle.Observer
|
||||
import com.airbnb.mvrx.activityViewModel
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationItemView
|
||||
|
@ -32,11 +35,13 @@ import im.vector.riotx.R
|
|||
import im.vector.riotx.core.extensions.commitTransactionNow
|
||||
import im.vector.riotx.core.glide.GlideApp
|
||||
import im.vector.riotx.core.platform.ToolbarConfigurable
|
||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.core.ui.views.KeysBackupBanner
|
||||
import im.vector.riotx.features.home.room.list.RoomListFragment
|
||||
import im.vector.riotx.features.home.room.list.RoomListParams
|
||||
import im.vector.riotx.features.home.room.list.UnreadCounterBadgeView
|
||||
import im.vector.riotx.features.popup.PopupAlertManager
|
||||
import im.vector.riotx.features.workers.signout.SignOutViewModel
|
||||
import kotlinx.android.synthetic.main.fragment_home_detail.*
|
||||
import timber.log.Timber
|
||||
|
@ -54,6 +59,8 @@ class HomeDetailFragment @Inject constructor(
|
|||
private val unreadCounterBadgeViews = arrayListOf<UnreadCounterBadgeView>()
|
||||
|
||||
private val viewModel: HomeDetailViewModel by fragmentViewModel()
|
||||
private val unknownDeviceDetectorSharedViewModel : UnknownDeviceDetectorSharedViewModel by activityViewModel()
|
||||
|
||||
private lateinit var sharedActionViewModel: HomeSharedActionViewModel
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_home_detail
|
||||
|
@ -77,6 +84,36 @@ class HomeDetailFragment @Inject constructor(
|
|||
viewModel.selectSubscribe(this, HomeDetailViewState::displayMode) { displayMode ->
|
||||
switchDisplayMode(displayMode)
|
||||
}
|
||||
|
||||
unknownDeviceDetectorSharedViewModel.subscribe {
|
||||
it.unknownSessions.invoke()?.let { unknownDevices ->
|
||||
Timber.v("## Detector - ${unknownDevices.size} Unknown sessions")
|
||||
unknownDevices.forEachIndexed { index, deviceInfo ->
|
||||
Timber.v("## Detector - #${index} deviceId:${deviceInfo.deviceId} lastSeenTs:${deviceInfo.lastSeenTs}")
|
||||
}
|
||||
if (it.canCrossSign && unknownDevices.isNotEmpty()) {
|
||||
val newest = unknownDevices.first()
|
||||
val uid = "ND_${newest.deviceId}"
|
||||
PopupAlertManager.cancelAlert(uid)
|
||||
PopupAlertManager.postVectorAlert(
|
||||
PopupAlertManager.VectorAlert(
|
||||
uid = uid,
|
||||
title = getString(R.string.new_session),
|
||||
description = getString(R.string.new_session_review),
|
||||
iconId = R.drawable.ic_shield_warning
|
||||
).apply {
|
||||
colorInt = ContextCompat.getColor(requireActivity(), R.color.riotx_accent)
|
||||
contentAction = Runnable {
|
||||
(weakCurrentActivity?.get() as? VectorBaseActivity)
|
||||
?.navigator
|
||||
?.requestSessionVerification(requireContext(), newest.deviceId ?: "")
|
||||
}
|
||||
dismissedAction = Runnable {}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onGroupChange(groupSummary: GroupSummary?) {
|
||||
|
|
|
@ -16,7 +16,15 @@
|
|||
|
||||
package im.vector.riotx.features.home
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.util.NoOpCancellable
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
|
||||
import im.vector.matrix.rx.rx
|
||||
import im.vector.matrix.rx.singleBuilder
|
||||
import im.vector.riotx.core.platform.VectorSharedActionViewModel
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import javax.inject.Inject
|
||||
|
||||
class HomeSharedActionViewModel @Inject constructor() : VectorSharedActionViewModel<HomeActivitySharedAction>() {
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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.riotx.features.home
|
||||
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.util.NoOpCancellable
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
|
||||
import im.vector.matrix.rx.rx
|
||||
import im.vector.matrix.rx.singleBuilder
|
||||
import im.vector.riotx.core.di.HasScreenInjector
|
||||
import im.vector.riotx.core.platform.EmptyAction
|
||||
import im.vector.riotx.core.platform.EmptyViewEvents
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
|
||||
data class UnknownDevicesState(
|
||||
val unknownSessions: Async<List<DeviceInfo>?> = Uninitialized,
|
||||
val canCrossSign: Boolean = false
|
||||
) : MvRxState
|
||||
|
||||
class UnknownDeviceDetectorSharedViewModel(session: Session, initialState: UnknownDevicesState) : VectorViewModel<UnknownDevicesState, EmptyAction, EmptyViewEvents>(initialState) {
|
||||
|
||||
init {
|
||||
session.rx().liveUserCryptoDevices(session.myUserId)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.switchMap { deviceList ->
|
||||
singleBuilder<DevicesListResponse> {
|
||||
session.cryptoService().getDevicesList(it)
|
||||
NoOpCancellable
|
||||
}.map { resp ->
|
||||
resp.devices?.filter { info ->
|
||||
deviceList.firstOrNull { info.deviceId == it.deviceId }?.isVerified?.not() ?: false
|
||||
}?.sortedByDescending { it.lastSeenTs }
|
||||
}
|
||||
.toObservable()
|
||||
}
|
||||
.execute { async ->
|
||||
copy(unknownSessions = async)
|
||||
}
|
||||
|
||||
session.rx().liveCrossSigningInfo(session.myUserId)
|
||||
.execute {
|
||||
copy(canCrossSign = session.cryptoService().crossSigningService().canCrossSign())
|
||||
}
|
||||
}
|
||||
|
||||
override fun handle(action: EmptyAction) {}
|
||||
|
||||
companion object : MvRxViewModelFactory<UnknownDeviceDetectorSharedViewModel, UnknownDevicesState> {
|
||||
override fun create(viewModelContext: ViewModelContext, state: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel? {
|
||||
val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession()
|
||||
return UnknownDeviceDetectorSharedViewModel(session, state)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -83,12 +83,12 @@ class DefaultNavigator @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun requestSessionVerification(context: Context) {
|
||||
override fun requestSessionVerification(context: Context, otherSessionId: String) {
|
||||
val session = sessionHolder.getSafeActiveSession() ?: return
|
||||
val pr = session.cryptoService().verificationService().requestKeyVerification(
|
||||
supportedVerificationMethodsProvider.provide(),
|
||||
session.myUserId,
|
||||
session.cryptoService().getUserDevices(session.myUserId).map { it.deviceId }
|
||||
listOf(otherSessionId)
|
||||
)
|
||||
if (context is VectorBaseActivity) {
|
||||
VerificationBottomSheet.withArgs(
|
||||
|
|
|
@ -30,7 +30,7 @@ interface Navigator {
|
|||
|
||||
fun performDeviceVerification(context: Context, otherUserId: String, sasTransactionId: String)
|
||||
|
||||
fun requestSessionVerification(context: Context)
|
||||
fun requestSessionVerification(context: Context, otherSessionId: String)
|
||||
|
||||
fun waitSessionVerification(context: Context)
|
||||
|
||||
|
|
|
@ -57,6 +57,8 @@ class VectorSettingsActivity : VectorBaseActivity(),
|
|||
when (intent.getIntExtra(EXTRA_DIRECT_ACCESS, EXTRA_DIRECT_ACCESS_ROOT)) {
|
||||
EXTRA_DIRECT_ACCESS_ADVANCED_SETTINGS ->
|
||||
replaceFragment(R.id.vector_settings_page, VectorSettingsAdvancedSettingsFragment::class.java, null, FRAGMENT_TAG)
|
||||
EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY ->
|
||||
replaceFragment(R.id.vector_settings_page, VectorSettingsSecurityPrivacyFragment::class.java, null, FRAGMENT_TAG)
|
||||
else ->
|
||||
replaceFragment(R.id.vector_settings_page, VectorSettingsRootFragment::class.java, null, FRAGMENT_TAG)
|
||||
}
|
||||
|
@ -116,6 +118,7 @@ class VectorSettingsActivity : VectorBaseActivity(),
|
|||
|
||||
const val EXTRA_DIRECT_ACCESS_ROOT = 0
|
||||
const val EXTRA_DIRECT_ACCESS_ADVANCED_SETTINGS = 1
|
||||
const val EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY = 2
|
||||
|
||||
private const val FRAGMENT_TAG = "VectorSettingsPreferencesFragment"
|
||||
}
|
||||
|
|
|
@ -78,6 +78,17 @@ class CrossSigningEpoxyController @Inject constructor(
|
|||
interactionListener?.onResetCrossSigningKeys()
|
||||
}
|
||||
}
|
||||
|
||||
bottomSheetVerificationActionItem {
|
||||
id("verify")
|
||||
title(stringProvider.getString(R.string.complete_security))
|
||||
titleColor(colorProvider.getColor(R.color.riotx_positive_accent))
|
||||
iconRes(R.drawable.ic_arrow_right)
|
||||
iconColor(colorProvider.getColor(R.color.riotx_positive_accent))
|
||||
listener {
|
||||
interactionListener?.verifySession()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (data.xSigningIsEnableInAccount) {
|
||||
genericItem {
|
||||
|
|
|
@ -14,6 +14,20 @@
|
|||
|
||||
<string name="refresh">Refresh</string>
|
||||
|
||||
|
||||
<string name="new_session">New Session</string>
|
||||
<string name="new_session_review">Tap to review & verify</string>
|
||||
<string name="verify_new_session_notice">Use this session to verify your new one, granting it access to encrypted messages.</string>
|
||||
<string name="verify_new_session_was_not_me">This wasn’t me</string>
|
||||
<string name="verify_new_session_compromized">Your account may be compromised</string>
|
||||
|
||||
<string name="verify_cancel_self_verification_from_untrusted">If you cancel, you won’t be able to read encrypted messages on this device, and other users won’t trust it</string>
|
||||
<string name="verify_cancel_self_verification_from_trusted">If you cancel, you won’t be able to read encrypted messages on your new device, and other users won’t trust it</string>
|
||||
|
||||
<string name="verify_not_me_self_verification">
|
||||
One of the following may be compromised:\n\n- Your password\n- Your homeserver\n- This device, or the other device\n- The internet connection either device is using\n\nWe recommend you change your password & recovery key in Settings immediately.
|
||||
</string>
|
||||
|
||||
<!-- END Strings added by Valere -->
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue