Basic Incoming request toast + cleaning

This commit is contained in:
Valere 2020-01-03 14:54:10 +01:00
parent 8400ab6efe
commit 8749e49e80
25 changed files with 189 additions and 1355 deletions

View File

@ -44,6 +44,7 @@ interface SasVerificationService {
fun getExistingVerificationRequest(otherUser: String, tid: String?): PendingVerificationRequest?
fun getExistingVerificationRequestInRoom(roomId: String, tid: String?): PendingVerificationRequest?
/**
* Shortcut for KeyVerificationStart.VERIF_METHOD_SAS
* @see beginKeyVerification

View File

@ -104,6 +104,7 @@ internal class DefaultRoomVerificationUpdateTask @Inject constructor(
// The verification is started from another device
Timber.v("## SAS Verification live observer: Transaction started by other device tid:${it.transactionID} ")
it.transactionID?.let { txId -> transactionsHandledByOtherDevice.add(txId) }
params.sasVerificationService.onRoomRequestHandledByOtherDevice(event)
}
}
} else if (EventType.KEY_VERIFICATION_READY == event.type) {
@ -112,11 +113,13 @@ internal class DefaultRoomVerificationUpdateTask @Inject constructor(
// The verification is started from another device
Timber.v("## SAS Verification live observer: Transaction started by other device tid:${it.transactionID} ")
it.transactionID?.let { txId -> transactionsHandledByOtherDevice.add(txId) }
params.sasVerificationService.onRoomRequestHandledByOtherDevice(event)
}
}
} else if (EventType.KEY_VERIFICATION_CANCEL == event.type || EventType.KEY_VERIFICATION_DONE == event.type) {
event.getClearContent().toModel<MessageRelationContent>()?.relatesTo?.eventId?.let {
transactionsHandledByOtherDevice.remove(it)
params.sasVerificationService.onRoomRequestHandledByOtherDevice(event)
}
}

View File

@ -216,7 +216,20 @@ internal class DefaultSasVerificationService @Inject constructor(
}
}
fun onRoomRequestReceived(event: Event) {
fun onRoomRequestHandledByOtherDevice(event: Event) {
val requestInfo = event.getClearContent().toModel<MessageRelationContent>()
?: return
val requestId = requestInfo.relatesTo?.eventId ?: return
getExistingVerificationRequestInRoom(event.roomId ?: "", requestId)?.let {
updatePendingRequest(
it.copy(
handledByOtherSession = true
)
)
}
}
suspend fun onRoomRequestReceived(event: Event) {
Timber.v("## SAS Verification request from ${event.senderId} in room ${event.roomId}")
val requestInfo = event.getClearContent().toModel<MessageVerificationRequestContent>()
?: return
@ -245,6 +258,7 @@ internal class DefaultSasVerificationService @Inject constructor(
ageLocalTs = event.ageLocalTs ?: System.currentTimeMillis(),
isIncoming = true,
otherUserId = senderId, // requestInfo.toUserId,
roomId = event.roomId,
transactionId = event.eventId,
requestInfo = requestInfo
)
@ -647,6 +661,16 @@ internal class DefaultSasVerificationService @Inject constructor(
}
}
override fun getExistingVerificationRequestInRoom(roomId: String, tid: String?): PendingVerificationRequest? {
synchronized(lock = pendingRequests) {
return tid?.let { tid ->
pendingRequests.flatMap { entry ->
entry.value.filter { it.roomId == roomId && it.transactionId == tid }
}.firstOrNull()
}
}
}
private fun getExistingTransactionsForUser(otherUser: String): Collection<VerificationTransaction>? {
synchronized(txMap) {
return txMap[otherUser]?.values
@ -723,6 +747,7 @@ internal class DefaultSasVerificationService @Inject constructor(
val verificationRequest = PendingVerificationRequest(
ageLocalTs = System.currentTimeMillis(),
isIncoming = false,
roomId = roomId,
localID = localID,
otherUserId = userId
)

View File

@ -27,11 +27,13 @@ data class PendingVerificationRequest(
val isIncoming: Boolean = false,
val localID: String = UUID.randomUUID().toString(),
val otherUserId: String,
val roomId: String?,
val transactionId: String? = null,
val requestInfo: MessageVerificationRequestContent? = null,
val readyInfo: VerificationInfoReady? = null,
val cancelConclusion: CancelCode? = null,
val isSuccessful : Boolean = false
val isSuccessful : Boolean = false,
val handledByOtherSession : Boolean = false
) {

View File

@ -47,9 +47,6 @@
android:label="@string/title_activity_settings"
android:windowSoftInputMode="adjustResize" />
<activity android:name=".features.media.VideoMediaViewerActivity" />
<activity
android:name=".features.crypto.verification.SASVerificationActivity"
android:label="@string/title_activity_verify_device" />
<activity
android:name=".features.crypto.keysbackup.restore.KeysBackupRestoreActivity"
android:label="@string/title_activity_keys_backup_setup" />

View File

@ -234,31 +234,6 @@ interface FragmentModule {
@FragmentKey(VectorSettingsIgnoredUsersFragment::class)
fun bindVectorSettingsIgnoredUsersFragment(fragment: VectorSettingsIgnoredUsersFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsDevicesFragment::class)
fun bindVectorSettingsDevicesFragment(fragment: VectorSettingsDevicesFragment): Fragment
@Binds
@IntoMap
@FragmentKey(SASVerificationIncomingFragment::class)
fun bindSASVerificationIncomingFragment(fragment: SASVerificationIncomingFragment): Fragment
@Binds
@IntoMap
@FragmentKey(SASVerificationShortCodeFragment::class)
fun bindSASVerificationShortCodeFragment(fragment: SASVerificationShortCodeFragment): Fragment
@Binds
@IntoMap
@FragmentKey(SASVerificationVerifiedFragment::class)
fun bindSASVerificationVerifiedFragment(fragment: SASVerificationVerifiedFragment): Fragment
@Binds
@IntoMap
@FragmentKey(SASVerificationStartFragment::class)
fun bindSASVerificationStartFragment(fragment: SASVerificationStartFragment): Fragment
@Binds
@IntoMap
@FragmentKey(PublicRoomsFragment::class)

View File

@ -27,7 +27,6 @@ import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreFromK
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreFromPassphraseViewModel
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreSharedViewModel
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupSharedViewModel
import im.vector.riotx.features.crypto.verification.SasVerificationViewModel
import im.vector.riotx.features.home.HomeSharedActionViewModel
import im.vector.riotx.features.home.room.detail.RoomDetailSharedActionViewModel
import im.vector.riotx.features.home.room.detail.timeline.action.MessageSharedActionViewModel
@ -61,11 +60,6 @@ interface ViewModelModule {
@ViewModelKey(EmojiChooserViewModel::class)
fun bindEmojiChooserViewModel(viewModel: EmojiChooserViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(SasVerificationViewModel::class)
fun bindSasVerificationViewModel(viewModel: SasVerificationViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(KeysBackupRestoreFromKeyViewModel::class)

View File

@ -101,10 +101,12 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
private lateinit var configurationViewModel: ConfigurationViewModel
private lateinit var sessionListener: SessionListener
protected lateinit var bugReporter: BugReporter
lateinit var rageShake: RageShake
private lateinit var rageShake: RageShake
lateinit var navigator: Navigator
private set
protected lateinit var navigator: Navigator
private lateinit var fragmentFactory: FragmentFactory
private lateinit var activeSessionHolder: ActiveSessionHolder
private lateinit var vectorPreferences: VectorPreferences
@ -210,8 +212,8 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
handleInvalidToken(globalError)
is GlobalError.ConsentNotGivenError ->
consentNotGivenHelper.displayDialog(globalError.consentUri,
activeSessionHolder.getActiveSession().sessionParams.homeServerConnectionConfig.homeServerUri.host
?: "")
activeSessionHolder.getActiveSession().sessionParams.homeServerConnectionConfig.homeServerUri.host
?: "")
}
}

View File

@ -33,13 +33,11 @@ import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
import im.vector.riotx.R
import im.vector.riotx.features.crypto.verification.SASVerificationActivity
import im.vector.riotx.features.popup.PopupAlertManager
import timber.log.Timber
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.Date
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.collections.ArrayList
@ -195,18 +193,19 @@ class KeyRequestHandler @Inject constructor(private val context: Context)
denyAllRequests(mappingKey)
}
alert.addButton(
context.getString(R.string.start_verification_short_label),
Runnable {
alert.weakCurrentActivity?.get()?.let {
val intent = SASVerificationActivity.outgoingIntent(it,
session?.myUserId ?: "",
userId, deviceId)
it.startActivity(intent)
}
},
false
)
// TODO send to the new profile page
// alert.addButton(
// context.getString(R.string.start_verification_short_label),
// Runnable {
// alert.weakCurrentActivity?.get()?.let {
// val intent = SASVerificationActivity.outgoingIntent(it,
// session?.myUserId ?: "",
// userId, deviceId)
// it.startActivity(intent)
// }
// },
// false
// )
alert.addButton(context.getString(R.string.share_without_verifying_short_label), Runnable {
shareAllSessions(mappingKey)

View File

@ -20,8 +20,13 @@ import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransaction
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
import im.vector.matrix.android.internal.crypto.verification.PendingVerificationRequest
import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.features.home.room.detail.RoomDetailActivity
import im.vector.riotx.features.home.room.detail.RoomDetailArgs
import im.vector.riotx.features.popup.PopupAlertManager
import im.vector.riotx.features.themes.ThemeUtils
import javax.inject.Inject
import javax.inject.Singleton
@ -48,46 +53,46 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context
override fun transactionUpdated(tx: SasVerificationTransaction) {
when (tx.state) {
SasVerificationTxState.OnStarted -> {
// Add a notification for every incoming request
val name = session?.getUser(tx.otherUserId)?.displayName
?: tx.otherUserId
val alert = PopupAlertManager.VectorAlert(
"kvr_${tx.transactionId}",
context.getString(R.string.sas_incoming_request_notif_title),
context.getString(R.string.sas_incoming_request_notif_content, name),
R.drawable.shield)
.apply {
contentAction = Runnable {
val intent = SASVerificationActivity.incomingIntent(context,
session?.myUserId ?: "",
tx.otherUserId,
tx.transactionId)
weakCurrentActivity?.get()?.startActivity(intent)
}
dismissedAction = Runnable {
tx.cancel()
}
addButton(
context.getString(R.string.ignore),
Runnable {
tx.cancel()
}
)
addButton(
context.getString(R.string.action_open),
Runnable {
val intent = SASVerificationActivity.incomingIntent(context,
session?.myUserId ?: "",
tx.otherUserId,
tx.transactionId)
weakCurrentActivity?.get()?.startActivity(intent)
}
)
// 10mn expiration
expirationTimestamp = System.currentTimeMillis() + (10 * 60 * 1000L)
}
PopupAlertManager.postVectorAlert(alert)
// // Add a notification for every incoming request
// val name = session?.getUser(tx.otherUserId)?.displayName
// ?: tx.otherUserId
//
// val alert = PopupAlertManager.VectorAlert(
// "kvr_${tx.transactionId}",
// context.getString(R.string.sas_incoming_request_notif_title),
// context.getString(R.string.sas_incoming_request_notif_content, name),
// R.drawable.shield)
// .apply {
// contentAction = Runnable {
// val intent = SASVerificationActivity.incomingIntent(context,
// session?.myUserId ?: "",
// tx.otherUserId,
// tx.transactionId)
// weakCurrentActivity?.get()?.startActivity(intent)
// }
// dismissedAction = Runnable {
// tx.cancel()
// }
// addButton(
// context.getString(R.string.ignore),
// Runnable {
// tx.cancel()
// }
// )
// addButton(
// context.getString(R.string.action_open),
// Runnable {
// val intent = SASVerificationActivity.incomingIntent(context,
// session?.myUserId ?: "",
// tx.otherUserId,
// tx.transactionId)
// weakCurrentActivity?.get()?.startActivity(intent)
// }
// )
// // 10mn expiration
// expirationTimestamp = System.currentTimeMillis() + (10 * 60 * 1000L)
// }
// PopupAlertManager.postVectorAlert(alert)
}
SasVerificationTxState.Cancelled,
SasVerificationTxState.OnCancelled,
@ -101,4 +106,54 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context
override fun markedAsManuallyVerified(userId: String, deviceId: String) {
}
override fun verificationRequestCreated(pr: PendingVerificationRequest) {
// For incoming request we should prompt (if not in activity where this request apply)
if (pr.isIncoming) {
val name = session?.getUser(pr.otherUserId)?.displayName
?: pr.otherUserId
val alert = PopupAlertManager.VectorAlert(
uniqueIdForVerificationRequest(pr),
context.getString(R.string.sas_incoming_request_notif_title),
"$name(${pr.otherUserId})",
R.drawable.ic_shield_black,
shouldBeDisplayedIn = { activity ->
if (activity is RoomDetailActivity) {
activity.intent?.extras?.getParcelable<RoomDetailArgs>(RoomDetailActivity.EXTRA_ROOM_DETAIL_ARGS)?.let {
it.roomId != pr.roomId
} ?: true
} else true
})
.apply {
contentAction = Runnable {
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
it.navigator.openRoom(it, pr.roomId ?: "", pr.transactionId)
}
}
dismissedAction = Runnable {
session?.getSasVerificationService()?.declineVerificationRequestInDMs(pr.otherUserId,
pr.requestInfo?.fromDevice ?: "",
pr.transactionId ?: "",
pr.roomId ?: ""
)
}
colorInt = ThemeUtils.getColor(context, R.attr.vctr_notice_secondary)
// 5mn expiration
expirationTimestamp = System.currentTimeMillis() + (5 * 60 * 1000L)
}
PopupAlertManager.postVectorAlert(alert)
}
}
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
// If an incoming request is readied (by another device?) we should discard the alert
if (pr.isIncoming && (pr.isReady || pr.handledByOtherSession)) {
PopupAlertManager.cancelAlert(uniqueIdForVerificationRequest(pr))
}
super.verificationRequestUpdated(pr)
}
private fun uniqueIdForVerificationRequest(pr: PendingVerificationRequest) =
"verificationRequest_${pr.transactionId}"
}

View File

@ -1,245 +0,0 @@
/*
* Copyright 2019 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
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.view.MenuItem
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.Observer
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction
import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRequest
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
import im.vector.riotx.R
import im.vector.riotx.core.extensions.commitTransaction
import im.vector.riotx.core.extensions.observeEvent
import im.vector.riotx.core.platform.SimpleFragmentActivity
import im.vector.riotx.core.platform.WaitingViewData
// TODO Deprecated("replaced by bottomsheet UX")
class SASVerificationActivity : SimpleFragmentActivity() {
companion object {
private const val EXTRA_MATRIX_ID = "EXTRA_MATRIX_ID"
private const val EXTRA_TRANSACTION_ID = "EXTRA_TRANSACTION_ID"
private const val EXTRA_OTHER_USER_ID = "EXTRA_OTHER_USER_ID"
private const val EXTRA_OTHER_DEVICE_ID = "EXTRA_OTHER_DEVICE_ID"
private const val EXTRA_IS_INCOMING = "EXTRA_IS_INCOMING"
/* ==========================================================================================
* INPUT
* ========================================================================================== */
fun incomingIntent(context: Context, matrixID: String, otherUserId: String, transactionID: String): Intent {
val intent = Intent(context, SASVerificationActivity::class.java)
intent.putExtra(EXTRA_MATRIX_ID, matrixID)
intent.putExtra(EXTRA_TRANSACTION_ID, transactionID)
intent.putExtra(EXTRA_OTHER_USER_ID, otherUserId)
intent.putExtra(EXTRA_IS_INCOMING, true)
return intent
}
fun outgoingIntent(context: Context, matrixID: String, otherUserId: String, otherDeviceId: String): Intent {
val intent = Intent(context, SASVerificationActivity::class.java)
intent.putExtra(EXTRA_MATRIX_ID, matrixID)
intent.putExtra(EXTRA_OTHER_DEVICE_ID, otherDeviceId)
intent.putExtra(EXTRA_OTHER_USER_ID, otherUserId)
intent.putExtra(EXTRA_IS_INCOMING, false)
return intent
}
/* ==========================================================================================
* OUTPUT
* ========================================================================================== */
fun getOtherUserId(intent: Intent?): String? {
return intent?.getStringExtra(EXTRA_OTHER_USER_ID)
}
fun getOtherDeviceId(intent: Intent?): String? {
return intent?.getStringExtra(EXTRA_OTHER_DEVICE_ID)
}
}
override fun getTitleRes() = R.string.title_activity_verify_device
private lateinit var viewModel: SasVerificationViewModel
override fun initUiAndData() {
super.initUiAndData()
viewModel = viewModelProvider.get(SasVerificationViewModel::class.java)
val transactionID: String? = intent.getStringExtra(EXTRA_TRANSACTION_ID)
if (isFirstCreation()) {
val isIncoming = intent.getBooleanExtra(EXTRA_IS_INCOMING, false)
if (isIncoming) {
// incoming always have a transaction id
viewModel.initIncoming(session, intent.getStringExtra(EXTRA_OTHER_USER_ID), transactionID)
} else {
viewModel.initOutgoing(session, intent.getStringExtra(EXTRA_OTHER_USER_ID), intent.getStringExtra(EXTRA_OTHER_DEVICE_ID))
}
if (isIncoming) {
val incoming = viewModel.transaction as? IncomingSasVerificationTransaction
when (incoming?.uxState) {
null,
IncomingSasVerificationTransaction.UxState.UNKNOWN,
IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT,
IncomingSasVerificationTransaction.UxState.WAIT_FOR_KEY_AGREEMENT -> {
supportActionBar?.setTitle(R.string.sas_incoming_request_title)
supportFragmentManager.commitTransaction {
setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
replace(R.id.container, SASVerificationIncomingFragment::class.java, null)
}
}
IncomingSasVerificationTransaction.UxState.WAIT_FOR_VERIFICATION,
IncomingSasVerificationTransaction.UxState.SHOW_SAS -> {
supportFragmentManager.commitTransaction {
setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
replace(R.id.container, SASVerificationShortCodeFragment::class.java, null)
}
}
IncomingSasVerificationTransaction.UxState.VERIFIED -> {
supportFragmentManager.commitTransaction {
setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
replace(R.id.container, SASVerificationVerifiedFragment::class.java, null)
}
}
IncomingSasVerificationTransaction.UxState.CANCELLED_BY_ME,
IncomingSasVerificationTransaction.UxState.CANCELLED_BY_OTHER -> {
viewModel.navigateCancel()
}
}
} else {
val outgoing = viewModel.transaction as? OutgoingSasVerificationRequest
// transaction can be null, as not yet created
when (outgoing?.uxState) {
null,
OutgoingSasVerificationRequest.UxState.UNKNOWN,
OutgoingSasVerificationRequest.UxState.WAIT_FOR_START,
OutgoingSasVerificationRequest.UxState.WAIT_FOR_KEY_AGREEMENT -> {
supportFragmentManager.commitTransaction {
setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
replace(R.id.container, SASVerificationStartFragment::class.java, null)
}
}
OutgoingSasVerificationRequest.UxState.SHOW_SAS,
OutgoingSasVerificationRequest.UxState.WAIT_FOR_VERIFICATION -> {
supportFragmentManager.commitTransaction {
setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
replace(R.id.container, SASVerificationShortCodeFragment::class.java, null)
}
}
OutgoingSasVerificationRequest.UxState.VERIFIED -> {
supportFragmentManager.commitTransaction {
setCustomAnimations(R.anim.no_anim, R.anim.exit_fade_out)
replace(R.id.container, SASVerificationVerifiedFragment::class.java, null)
}
}
OutgoingSasVerificationRequest.UxState.CANCELLED_BY_ME,
OutgoingSasVerificationRequest.UxState.CANCELLED_BY_OTHER -> {
viewModel.navigateCancel()
}
}
}
}
viewModel.navigateEvent.observeEvent(this) { uxStateEvent ->
when (uxStateEvent) {
SasVerificationViewModel.NAVIGATE_FINISH -> {
finish()
}
SasVerificationViewModel.NAVIGATE_FINISH_SUCCESS -> {
val dataResult = Intent()
dataResult.putExtra(EXTRA_OTHER_DEVICE_ID, viewModel.otherDeviceId)
dataResult.putExtra(EXTRA_OTHER_USER_ID, viewModel.otherUserId)
setResult(Activity.RESULT_OK, dataResult)
finish()
}
SasVerificationViewModel.NAVIGATE_SAS_DISPLAY -> {
supportFragmentManager.commitTransaction {
setCustomAnimations(R.anim.enter_from_right, R.anim.exit_fade_out)
replace(R.id.container, SASVerificationShortCodeFragment::class.java, null)
}
}
SasVerificationViewModel.NAVIGATE_SUCCESS -> {
supportFragmentManager.commitTransaction {
setCustomAnimations(R.anim.enter_from_right, R.anim.exit_fade_out)
replace(R.id.container, SASVerificationVerifiedFragment::class.java, null)
}
}
SasVerificationViewModel.NAVIGATE_CANCELLED -> {
val isCancelledByMe = viewModel.transaction?.state == SasVerificationTxState.Cancelled
val humanReadableReason = when (viewModel.transaction?.cancelledReason) {
CancelCode.User -> getString(R.string.sas_error_m_user)
CancelCode.Timeout -> getString(R.string.sas_error_m_timeout)
CancelCode.UnknownTransaction -> getString(R.string.sas_error_m_unknown_transaction)
CancelCode.UnknownMethod -> getString(R.string.sas_error_m_unknown_method)
CancelCode.MismatchedCommitment -> getString(R.string.sas_error_m_mismatched_commitment)
CancelCode.MismatchedSas -> getString(R.string.sas_error_m_mismatched_sas)
CancelCode.UnexpectedMessage -> getString(R.string.sas_error_m_unexpected_message)
CancelCode.InvalidMessage -> getString(R.string.sas_error_m_invalid_message)
CancelCode.MismatchedKeys -> getString(R.string.sas_error_m_key_mismatch)
// Use user error
CancelCode.UserMismatchError -> getString(R.string.sas_error_m_user_error)
null -> getString(R.string.sas_error_unknown)
}
val message =
if (isCancelledByMe) getString(R.string.sas_cancelled_by_me, humanReadableReason)
else getString(R.string.sas_cancelled_by_other, humanReadableReason)
// Show a dialog
if (!this.isFinishing) {
AlertDialog.Builder(this)
.setTitle(R.string.sas_cancelled_dialog_title)
.setMessage(message)
.setCancelable(false)
.setPositiveButton(R.string.ok) { _, _ ->
// nop
finish()
}
.show()
}
}
}
}
viewModel.loadingLiveEvent.observe(this, Observer {
if (it == null) {
hideWaitingView()
} else {
val status = if (it == -1) "" else getString(it)
updateWaitingView(WaitingViewData(status, isIndeterminate = true))
}
})
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
// we want to cancel the transaction
viewModel.cancelTransaction()
}
return super.onOptionsItemSelected(item)
}
override fun onBackPressed() {
// we want to cancel the transaction
viewModel.cancelTransaction()
}
}

View File

@ -1,100 +0,0 @@
/*
* Copyright 2019 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
import android.os.Bundle
import android.widget.ImageView
import android.widget.TextView
import androidx.lifecycle.Observer
import butterknife.BindView
import butterknife.OnClick
import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.features.home.AvatarRenderer
import javax.inject.Inject
// TODO Deprecated("replaced by bottomsheet UX")
class SASVerificationIncomingFragment @Inject constructor(
private var avatarRenderer: AvatarRenderer
) : VectorBaseFragment() {
@BindView(R.id.sas_incoming_request_user_display_name)
lateinit var otherUserDisplayNameTextView: TextView
@BindView(R.id.sas_incoming_request_user_id)
lateinit var otherUserIdTextView: TextView
@BindView(R.id.sas_incoming_request_user_device)
lateinit var otherDeviceTextView: TextView
@BindView(R.id.sas_incoming_request_user_avatar)
lateinit var avatarImageView: ImageView
override fun getLayoutResId() = R.layout.fragment_sas_verification_incoming_request
private lateinit var viewModel: SasVerificationViewModel
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = activityViewModelProvider.get(SasVerificationViewModel::class.java)
otherUserDisplayNameTextView.text = viewModel.otherUser?.displayName ?: viewModel.otherUserId
otherUserIdTextView.text = viewModel.otherUserId
otherDeviceTextView.text = viewModel.otherDeviceId
viewModel.otherUser?.let {
avatarRenderer.render(it.toMatrixItem(), avatarImageView)
} ?: run {
// Fallback to what we know
avatarRenderer.render(MatrixItem.UserItem(viewModel.otherUserId ?: "", viewModel.otherUserId), avatarImageView)
}
viewModel.transactionState.observe(viewLifecycleOwner, Observer {
val uxState = (viewModel.transaction as? IncomingSasVerificationTransaction)?.uxState
when (uxState) {
IncomingSasVerificationTransaction.UxState.SHOW_ACCEPT -> {
viewModel.loadingLiveEvent.value = null
}
IncomingSasVerificationTransaction.UxState.WAIT_FOR_KEY_AGREEMENT -> {
viewModel.loadingLiveEvent.value = R.string.sas_waiting_for_partner
}
IncomingSasVerificationTransaction.UxState.SHOW_SAS -> {
viewModel.shortCodeReady()
}
IncomingSasVerificationTransaction.UxState.CANCELLED_BY_ME,
IncomingSasVerificationTransaction.UxState.CANCELLED_BY_OTHER -> {
viewModel.loadingLiveEvent.value = null
viewModel.navigateCancel()
}
else -> Unit
}
})
}
@OnClick(R.id.sas_request_continue_button)
fun didAccept() {
viewModel.acceptTransaction()
}
@OnClick(R.id.sas_request_cancel_button)
fun didCancel() {
viewModel.cancelTransaction()
}
}

View File

@ -1,170 +0,0 @@
/*
* Copyright 2019 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
import android.os.Bundle
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import butterknife.BindView
import butterknife.OnClick
import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction
import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRequest
import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorBaseFragment
import javax.inject.Inject
// TODO Deprecated("replaced by bottomsheet UX")
class SASVerificationShortCodeFragment @Inject constructor(): VectorBaseFragment() {
private lateinit var viewModel: SasVerificationViewModel
@BindView(R.id.sas_decimal_code)
lateinit var decimalTextView: TextView
@BindView(R.id.sas_emoji_description)
lateinit var descriptionTextView: TextView
@BindView(R.id.sas_emoji_grid)
lateinit var emojiGrid: ViewGroup
@BindView(R.id.emoji0)
lateinit var emoji0View: ViewGroup
@BindView(R.id.emoji1)
lateinit var emoji1View: ViewGroup
@BindView(R.id.emoji2)
lateinit var emoji2View: ViewGroup
@BindView(R.id.emoji3)
lateinit var emoji3View: ViewGroup
@BindView(R.id.emoji4)
lateinit var emoji4View: ViewGroup
@BindView(R.id.emoji5)
lateinit var emoji5View: ViewGroup
@BindView(R.id.emoji6)
lateinit var emoji6View: ViewGroup
override fun getLayoutResId() = R.layout.fragment_sas_verification_display_code
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = activityViewModelProvider.get(SasVerificationViewModel::class.java)
viewModel.transaction?.let {
if (it.supportsEmoji()) {
val emojicodes = it.getEmojiCodeRepresentation()
emojicodes.forEachIndexed { index, emojiRepresentation ->
when (index) {
0 -> {
emoji0View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
emoji0View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId)
}
1 -> {
emoji1View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
emoji1View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId)
}
2 -> {
emoji2View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
emoji2View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId)
}
3 -> {
emoji3View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
emoji3View.findViewById<TextView>(R.id.item_emoji_name_tv)?.setText(emojiRepresentation.nameResId)
}
4 -> {
emoji4View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
emoji4View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId)
}
5 -> {
emoji5View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
emoji5View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId)
}
6 -> {
emoji6View.findViewById<TextView>(R.id.item_emoji_tv).text = emojiRepresentation.emoji
emoji6View.findViewById<TextView>(R.id.item_emoji_name_tv).setText(emojiRepresentation.nameResId)
}
}
}
}
// decimal is at least supported
decimalTextView.text = it.getDecimalCodeRepresentation()
if (it.supportsEmoji()) {
descriptionTextView.text = getString(R.string.sas_emoji_description)
decimalTextView.isVisible = false
emojiGrid.isVisible = true
} else {
descriptionTextView.text = getString(R.string.sas_decimal_description)
decimalTextView.isVisible = true
emojiGrid.isInvisible = true
}
}
viewModel.transactionState.observe(viewLifecycleOwner, Observer {
if (viewModel.transaction is IncomingSasVerificationTransaction) {
val uxState = (viewModel.transaction as IncomingSasVerificationTransaction).uxState
when (uxState) {
IncomingSasVerificationTransaction.UxState.SHOW_SAS -> {
viewModel.loadingLiveEvent.value = null
}
IncomingSasVerificationTransaction.UxState.VERIFIED -> {
viewModel.loadingLiveEvent.value = null
viewModel.deviceIsVerified()
}
IncomingSasVerificationTransaction.UxState.CANCELLED_BY_ME,
IncomingSasVerificationTransaction.UxState.CANCELLED_BY_OTHER -> {
viewModel.loadingLiveEvent.value = null
viewModel.navigateCancel()
}
else -> {
viewModel.loadingLiveEvent.value = R.string.sas_waiting_for_partner
}
}
} else if (viewModel.transaction is OutgoingSasVerificationRequest) {
val uxState = (viewModel.transaction as OutgoingSasVerificationRequest).uxState
when (uxState) {
OutgoingSasVerificationRequest.UxState.SHOW_SAS -> {
viewModel.loadingLiveEvent.value = null
}
OutgoingSasVerificationRequest.UxState.VERIFIED -> {
viewModel.loadingLiveEvent.value = null
viewModel.deviceIsVerified()
}
OutgoingSasVerificationRequest.UxState.CANCELLED_BY_ME,
OutgoingSasVerificationRequest.UxState.CANCELLED_BY_OTHER -> {
viewModel.loadingLiveEvent.value = null
viewModel.navigateCancel()
}
else -> {
viewModel.loadingLiveEvent.value = R.string.sas_waiting_for_partner
}
}
}
})
}
@OnClick(R.id.sas_request_continue_button)
fun didAccept() {
viewModel.confirmEmojiSame()
}
@OnClick(R.id.sas_request_cancel_button)
fun didCancel() {
viewModel.cancelTransaction()
}
}

View File

@ -1,120 +0,0 @@
/*
* Copyright 2019 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
import android.os.Bundle
import android.view.ViewGroup
import android.widget.Button
import android.widget.ProgressBar
import android.widget.TextView
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import androidx.transition.TransitionManager
import butterknife.BindView
import butterknife.OnClick
import im.vector.matrix.android.api.session.crypto.sas.OutgoingSasVerificationRequest
import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.core.platform.VectorBaseFragment
import javax.inject.Inject
// TODO Deprecated("replaced by bottomsheet UX")
class SASVerificationStartFragment @Inject constructor(): VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_sas_verification_start
private lateinit var viewModel: SasVerificationViewModel
@BindView(R.id.rootLayout)
lateinit var rootLayout: ViewGroup
@BindView(R.id.sas_start_button)
lateinit var startButton: Button
@BindView(R.id.sas_start_button_loading)
lateinit var startButtonLoading: ProgressBar
@BindView(R.id.sas_verifying_keys)
lateinit var loadingText: TextView
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = activityViewModelProvider.get(SasVerificationViewModel::class.java)
viewModel.transactionState.observe(viewLifecycleOwner, Observer {
val uxState = (viewModel.transaction as? OutgoingSasVerificationRequest)?.uxState
when (uxState) {
OutgoingSasVerificationRequest.UxState.WAIT_FOR_KEY_AGREEMENT -> {
// display loading
TransitionManager.beginDelayedTransition(this.rootLayout)
this.loadingText.isVisible = true
this.startButton.isInvisible = true
this.startButtonLoading.isVisible = true
this.startButtonLoading.animate()
}
OutgoingSasVerificationRequest.UxState.SHOW_SAS -> {
viewModel.shortCodeReady()
}
OutgoingSasVerificationRequest.UxState.CANCELLED_BY_ME,
OutgoingSasVerificationRequest.UxState.CANCELLED_BY_OTHER -> {
viewModel.navigateCancel()
}
else -> {
TransitionManager.beginDelayedTransition(this.rootLayout)
this.loadingText.isVisible = false
this.startButton.isVisible = true
this.startButtonLoading.isVisible = false
}
}
})
}
@OnClick(R.id.sas_start_button)
fun doStart() {
viewModel.beginSasKeyVerification()
}
@OnClick(R.id.sas_legacy_verification)
fun doLegacy() {
(requireActivity() as VectorBaseActivity).notImplemented()
/*
viewModel.session.crypto?.getDeviceInfo(viewModel.otherUserMxItem ?: "", viewModel.otherDeviceId
?: "", object : SimpleApiCallback<MXDeviceInfo>() {
override fun onSuccess(info: MXDeviceInfo?) {
info?.let {
CommonActivityUtils.displayDeviceVerificationDialogLegacy(it, it.userId, viewModel.session, activity, object : YesNoListener {
override fun yes() {
viewModel.manuallyVerified()
}
override fun no() {
}
})
}
}
})
*/
}
@OnClick(R.id.sas_cancel_button)
fun doCancel() {
// Transaction may be started, or not
viewModel.cancelTransaction()
}
}

View File

@ -1,41 +0,0 @@
/*
* Copyright 2019 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
import android.os.Bundle
import butterknife.OnClick
import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorBaseFragment
import javax.inject.Inject
// TODO Deprecated("replaced by bottomsheet UX")
class SASVerificationVerifiedFragment @Inject constructor() : VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_sas_verification_verified
private lateinit var viewModel: SasVerificationViewModel
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = activityViewModelProvider.get(SasVerificationViewModel::class.java)
}
@OnClick(R.id.sas_verification_verified_done_button)
fun onDone() {
viewModel.finishSuccess()
}
}

View File

@ -1,153 +0,0 @@
/*
* Copyright 2019 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
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransaction
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
import im.vector.matrix.android.api.session.user.model.User
import im.vector.riotx.core.utils.LiveEvent
import javax.inject.Inject
// TODO Deprecated("replaced by bottomsheet UX")
class SasVerificationViewModel @Inject constructor() : ViewModel(),
SasVerificationService.SasVerificationListener {
companion object {
const val NAVIGATE_FINISH = "NAVIGATE_FINISH"
const val NAVIGATE_FINISH_SUCCESS = "NAVIGATE_FINISH_SUCCESS"
const val NAVIGATE_SAS_DISPLAY = "NAVIGATE_SAS_DISPLAY"
const val NAVIGATE_SUCCESS = "NAVIGATE_SUCCESS"
const val NAVIGATE_CANCELLED = "NAVIGATE_CANCELLED"
}
private lateinit var sasVerificationService: SasVerificationService
var otherUserId: String? = null
var otherDeviceId: String? = null
var otherUser: User? = null
var transaction: SasVerificationTransaction? = null
var transactionState: MutableLiveData<SasVerificationTxState> = MutableLiveData()
init {
// Force a first observe
transactionState.value = null
}
private var _navigateEvent: MutableLiveData<LiveEvent<String>> = MutableLiveData()
val navigateEvent: LiveData<LiveEvent<String>>
get() = _navigateEvent
var loadingLiveEvent: MutableLiveData<Int> = MutableLiveData()
var transactionID: String? = null
set(value) {
if (value != null) {
transaction = sasVerificationService.getExistingTransaction(otherUserId!!, value)
transactionState.value = transaction?.state
otherDeviceId = transaction?.otherDeviceId
}
field = value
}
fun initIncoming(session: Session, otherUserId: String, transactionID: String?) {
this.sasVerificationService = session.getSasVerificationService()
this.otherUserId = otherUserId
this.transactionID = transactionID
this.sasVerificationService.addListener(this)
this.otherUser = session.getUser(otherUserId)
if (transactionID == null || transaction == null) {
// sanity, this transaction is not known anymore
_navigateEvent.value = LiveEvent(NAVIGATE_FINISH)
}
}
fun initOutgoing(session: Session, otherUserId: String, otherDeviceId: String) {
this.sasVerificationService = session.getSasVerificationService()
this.otherUserId = otherUserId
this.otherDeviceId = otherDeviceId
this.sasVerificationService.addListener(this)
this.otherUser = session.getUser(otherUserId)
}
fun beginSasKeyVerification() {
val verificationSAS = sasVerificationService.beginKeyVerificationSAS(otherUserId!!, otherDeviceId!!)
this.transactionID = verificationSAS
}
override fun transactionCreated(tx: SasVerificationTransaction) {
}
override fun transactionUpdated(tx: SasVerificationTransaction) {
if (transactionID == tx.transactionId) {
transactionState.value = tx.state
}
}
override fun markedAsManuallyVerified(userId: String, deviceId: String) {
}
fun cancelTransaction() {
transaction?.cancel()
_navigateEvent.value = LiveEvent(NAVIGATE_FINISH)
}
fun finishSuccess() {
_navigateEvent.value = LiveEvent(NAVIGATE_FINISH_SUCCESS)
}
fun manuallyVerified() {
if (otherUserId != null && otherDeviceId != null) {
sasVerificationService.markedLocallyAsManuallyVerified(otherUserId!!, otherDeviceId!!)
}
_navigateEvent.value = LiveEvent(NAVIGATE_FINISH_SUCCESS)
}
fun acceptTransaction() {
(transaction as? IncomingSasVerificationTransaction)?.performAccept()
}
fun confirmEmojiSame() {
transaction?.userHasVerifiedShortCode()
}
fun shortCodeReady() {
loadingLiveEvent.value = null
_navigateEvent.value = LiveEvent(NAVIGATE_SAS_DISPLAY)
}
fun deviceIsVerified() {
loadingLiveEvent.value = null
_navigateEvent.value = LiveEvent(NAVIGATE_SUCCESS)
}
fun navigateCancel() {
_navigateEvent.value = LiveEvent(NAVIGATE_CANCELLED)
}
override fun onCleared() {
super.onCleared()
if (::sasVerificationService.isInitialized) {
sasVerificationService.removeListener(this)
}
}
}

View File

@ -67,6 +67,6 @@ sealed class RoomDetailAction : VectorViewModelAction {
data class AcceptVerificationRequest(val transactionId: String, val otherUserId: String, val otherdDeviceId: String) : RoomDetailAction()
data class DeclineVerificationRequest(val transactionId: String, val otherUserId: String, val otherdDeviceId: String) : RoomDetailAction()
data class RequestVerification(val userId: String) : RoomDetailAction()
data class ResumeVerification(val transactionId: String, val otherUserId: String? = null, val otherdDeviceId: String? = null) : RoomDetailAction()
}

View File

@ -109,7 +109,7 @@ class RoomDetailActivity : VectorBaseActivity(), ToolbarConfigurable {
companion object {
private const val EXTRA_ROOM_DETAIL_ARGS = "EXTRA_ROOM_DETAIL_ARGS"
const val EXTRA_ROOM_DETAIL_ARGS = "EXTRA_ROOM_DETAIL_ARGS"
fun newIntent(context: Context, roomDetailArgs: RoomDetailArgs): Intent {
return Intent(context, RoomDetailActivity::class.java).apply {

View File

@ -849,6 +849,15 @@ class RoomDetailFragment @Inject constructor(
data.transactionId
).show(parentFragmentManager, "REQ")
}
is RoomDetailAction.ResumeVerification -> {
val otherUserId = data.otherUserId ?: return
VerificationBottomSheet().apply {
arguments = Bundle().apply {
putParcelable(MvRx.KEY_ARG, VerificationBottomSheet.VerificationArgs(
otherUserId, data.transactionId, roomId = roomDetailArgs.roomId))
}
}.show(parentFragmentManager, "REQ")
}
}
}
}
@ -998,6 +1007,9 @@ class RoomDetailFragment @Inject constructor(
}
override fun onEventCellClicked(informationData: MessageInformationData, messageContent: MessageContent?, view: View) {
if (messageContent is MessageVerificationRequestContent) {
roomDetailViewModel.handle(RoomDetailAction.ResumeVerification(informationData.eventId))
}
}
override fun onEventLongClicked(informationData: MessageInformationData, messageContent: MessageContent?, view: View): Boolean {

View File

@ -187,6 +187,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
is RoomDetailAction.AcceptVerificationRequest -> handleAcceptVerification(action)
is RoomDetailAction.DeclineVerificationRequest -> handleDeclineVerification(action)
is RoomDetailAction.RequestVerification -> handleRequestVerification(action)
is RoomDetailAction.ResumeVerification -> handleResumeRequestVerification(action)
}
}
@ -824,6 +825,18 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
_requestLiveData.postValue(LiveEvent(Success(action)))
}
private fun handleResumeRequestVerification(action: RoomDetailAction.ResumeVerification) {
// Check if this request is still active and handled by me
session.getSasVerificationService().getExistingVerificationRequestInRoom(room.roomId, action.transactionId)?.let {
if (it.handledByOtherSession) return
if (!it.isFinished) {
_requestLiveData.postValue(LiveEvent(Success(action.copy(
otherUserId = it.otherUserId
))))
}
}
}
private fun observeSyncState() {
session.rx()
.liveSyncState()

View File

@ -20,12 +20,12 @@ import android.os.Build
import android.os.Handler
import android.os.Looper
import android.view.View
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import com.tapadoo.alerter.Alerter
import com.tapadoo.alerter.OnHideAlertListener
import im.vector.riotx.R
import im.vector.riotx.features.crypto.verification.SASVerificationActivity
import timber.log.Timber
import java.lang.ref.WeakReference
@ -78,8 +78,7 @@ object PopupAlertManager {
setLightStatusBar()
}
}
if (shouldIgnoreActivity(activity)) {
if (currentAlerter?.shouldBeDisplayedIn?.invoke(activity) == false) {
return
}
@ -108,8 +107,6 @@ object PopupAlertManager {
}
}
private fun shouldIgnoreActivity(activity: Activity) = activity is SASVerificationActivity
private fun displayNextIfPossible() {
val currentActivity = weakCurrentActivity?.get()
if (Alerter.isShowing || currentActivity == null) {
@ -209,7 +206,13 @@ object PopupAlertManager {
})
.enableSwipeToDismiss()
.enableInfiniteDuration(true)
.setBackgroundColorRes(alert.colorRes ?: R.color.notification_accent_color)
.apply {
if (alert.colorInt != null) {
setBackgroundColorInt(alert.colorInt!!)
} else {
setBackgroundColorRes(alert.colorRes ?: R.color.notification_accent_color)
}
}
.show()
}
@ -229,7 +232,8 @@ object PopupAlertManager {
data class VectorAlert(val uid: String,
val title: String,
val description: String,
@DrawableRes val iconId: Int?) {
@DrawableRes val iconId: Int?,
val shouldBeDisplayedIn: ((Activity) -> Boolean)? = null) {
data class Button(val title: String, val action: Runnable, val autoClose: Boolean)
@ -250,5 +254,8 @@ object PopupAlertManager {
@ColorRes
var colorRes: Int? = null
@ColorInt
var colorInt: Int? = null
}
}

View File

@ -1,132 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:colorBackground">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/sas_emoji_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/layout_horizontal_margin"
android:layout_marginTop="@dimen/layout_vertical_margin"
android:gravity="center"
android:textColor="?riotx_text_primary"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/sas_emoji_description" />
<TextView
android:id="@+id/sas_emoji_description_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/layout_vertical_margin"
android:gravity="center"
android:text="@string/sas_security_advise"
android:textColor="?riotx_text_secondary"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/sas_emoji_description" />
<TextView
android:id="@+id/sas_decimal_code"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="?riotx_text_primary"
android:textSize="28sp"
android:textStyle="bold"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="@+id/sas_emoji_grid"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/sas_emoji_grid"
tools:text="1234-4320-3905"
tools:visibility="visible" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/sas_emoji_grid"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/layout_vertical_margin"
android:visibility="invisible"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sas_emoji_description_2"
tools:visibility="visible">
<include
android:id="@+id/emoji0"
layout="@layout/item_emoji_verif" />
<include
android:id="@+id/emoji1"
layout="@layout/item_emoji_verif" />
<include
android:id="@+id/emoji2"
layout="@layout/item_emoji_verif" />
<include
android:id="@+id/emoji3"
layout="@layout/item_emoji_verif" />
<include
android:id="@+id/emoji4"
layout="@layout/item_emoji_verif" />
<include
android:id="@+id/emoji5"
layout="@layout/item_emoji_verif" />
<include
android:id="@+id/emoji6"
layout="@layout/item_emoji_verif" />
<androidx.constraintlayout.helper.widget.Flow
android:id="@+id/sas_emoji_grid_flow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:constraint_referenced_ids="emoji0,emoji1,emoji2,emoji3,emoji4,emoji5,emoji6"
app:flow_horizontalBias="0.5"
app:flow_horizontalGap="16dp"
app:flow_horizontalStyle="packed"
app:flow_verticalBias="0"
app:flow_verticalGap="8dp"
app:flow_wrapMode="chain"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/sas_request_continue_button"
style="@style/VectorButtonStyle"
android:layout_margin="@dimen/layout_vertical_margin"
android:minWidth="160dp"
android:text="@string/_continue"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sas_emoji_grid" />
<com.google.android.material.button.MaterialButton
android:id="@+id/sas_request_cancel_button"
style="@style/VectorButtonStyleText"
android:layout_margin="@dimen/layout_vertical_margin"
android:text="@string/cancel"
app:layout_constraintEnd_toStartOf="@+id/sas_request_continue_button"
app:layout_constraintTop_toBottomOf="@+id/sas_emoji_grid" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View File

@ -1,132 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/sas_incoming_request_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="16dp"
android:gravity="center"
android:text="@string/sas_incoming_request_title"
android:textAlignment="center"
android:textColor="?riotx_text_primary"
android:textSize="17sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/sas_incoming_request_user_avatar"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_marginTop="@dimen/layout_vertical_margin"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sas_incoming_request_title"
tools:src="@tools:sample/avatars" />
<TextView
android:id="@+id/sas_incoming_request_user_display_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textAlignment="center"
android:textColor="?riotx_text_primary"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/sas_incoming_request_user_avatar"
tools:text="User name" />
<TextView
android:id="@+id/sas_incoming_request_user_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAlignment="center"
android:textColor="?riotx_text_secondary"
android:textSize="13sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/sas_incoming_request_user_display_name"
tools:text="\@foo:matrix.org" />
<TextView
android:id="@+id/sas_incoming_request_user_device"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textAlignment="center"
android:textColor="?riotx_text_secondary"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/sas_incoming_request_user_id"
tools:text="Device: Mobile" />
<TextView
android:id="@+id/sas_incoming_request_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/layout_vertical_margin"
android:text="@string/sas_incoming_request_description"
android:textAlignment="center"
android:textColor="?riotx_text_secondary"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/sas_incoming_request_user_device" />
<TextView
android:id="@+id/sas_incoming_request_description_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/layout_vertical_margin"
android:text="@string/sas_incoming_request_description_2"
android:textAlignment="center"
android:textColor="?riotx_text_secondary"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/sas_incoming_request_description" />
<com.google.android.material.button.MaterialButton
android:id="@+id/sas_request_continue_button"
style="@style/VectorButtonStyle"
android:layout_marginTop="@dimen/layout_vertical_margin_big"
android:layout_marginEnd="@dimen/layout_vertical_margin"
android:layout_marginRight="@dimen/layout_vertical_margin"
android:layout_marginBottom="@dimen/layout_vertical_margin"
android:minWidth="160dp"
android:text="@string/_continue"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sas_incoming_request_description_2" />
<com.google.android.material.button.MaterialButton
android:id="@+id/sas_request_cancel_button"
style="@style/VectorButtonStyleText"
android:layout_gravity="end"
android:layout_marginEnd="@dimen/layout_vertical_margin"
android:layout_marginRight="@dimen/layout_vertical_margin"
android:text="@string/cancel"
app:layout_constraintEnd_toStartOf="@+id/sas_request_continue_button"
app:layout_constraintTop_toTopOf="@+id/sas_request_continue_button" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View File

@ -1,89 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/sas_verification_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/sas_verify_title"
android:textColor="?riotx_text_primary"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/sas_verification_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/sas_security_advise"
android:textColor="?riotx_text_secondary" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/sas_cancel_button"
style="@style/VectorButtonStyleText"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/cancel" />
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/layout_horizontal_margin"
android:layout_marginRight="@dimen/layout_horizontal_margin">
<com.google.android.material.button.MaterialButton
android:id="@+id/sas_start_button"
style="@style/VectorButtonStyle"
android:minWidth="160dp"
android:text="@string/sas_verify_start_button_title" />
<ProgressBar
android:id="@+id/sas_start_button_loading"
android:layout_width="19dp"
android:layout_height="20dp"
android:layout_gravity="center"
android:indeterminate="true"
android:visibility="gone"
tools:visibility="visible" />
</FrameLayout>
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/sas_legacy_verification"
style="@style/VectorButtonStyleText"
android:layout_gravity="end"
android:layout_margin="@dimen/layout_horizontal_margin"
android:text="@string/sas_legacy_verification_button_title"
android:visibility="visible" />
<TextView
android:id="@+id/sas_verifying_keys"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/sas_verifying_keys"
android:textColor="?riotx_text_secondary"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
</ScrollView>

View File

@ -1,69 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:colorBackground">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/sas_verification_verified_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="16dp"
android:text="@string/sas_verified"
android:textColor="?riotx_text_primary"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/sas_verification_verified_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/layout_vertical_margin"
android:gravity="center"
android:text="@string/sas_verified_successful"
android:textColor="?riotx_text_secondary"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/sas_verification_verified_title" />
<TextView
android:id="@+id/sas_verification_verified_description_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/layout_vertical_margin"
android:gravity="center"
android:text="@string/sas_verified_successful_description"
android:textColor="?riotx_text_secondary"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/sas_verification_verified_description" />
<com.google.android.material.button.MaterialButton
android:id="@+id/sas_verification_verified_done_button"
style="@style/VectorButtonStyle"
android:layout_marginTop="@dimen/layout_vertical_margin_big"
android:layout_marginEnd="@dimen/layout_vertical_margin"
android:layout_marginRight="@dimen/layout_vertical_margin"
android:layout_marginBottom="@dimen/layout_vertical_margin"
android:minWidth="160dp"
android:text="@string/sas_got_it"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sas_verification_verified_description_2" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>