mirror of
https://github.com/vector-im/riotX-android
synced 2025-10-06 00:02:48 +02:00
Compare commits
9 Commits
feature/ad
...
arb/issues
Author | SHA1 | Date | |
---|---|---|---|
|
ade833da21 | ||
|
c64a786446 | ||
|
279e45dbc4 | ||
|
7ba377e84d | ||
|
764b8ae897 | ||
|
79f6e64a76 | ||
|
d3423bdf2b | ||
|
3fceaa3bb4 | ||
|
91c47b0ff9 |
1
changelog.d/4025.bugfix
Normal file
1
changelog.d/4025.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
Can't get out of a verification bottom sheet dialog.
|
@@ -17,6 +17,7 @@
|
||||
|
||||
package im.vector.app.core.di
|
||||
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentFactory
|
||||
import dagger.Binds
|
||||
@@ -40,7 +41,7 @@ import im.vector.app.features.crypto.recover.BootstrapSaveRecoveryKeyFragment
|
||||
import im.vector.app.features.crypto.recover.BootstrapSetupRecoveryKeyFragment
|
||||
import im.vector.app.features.crypto.recover.BootstrapWaitingFragment
|
||||
import im.vector.app.features.crypto.verification.QuadSLoadingFragment
|
||||
import im.vector.app.features.crypto.verification.cancel.VerificationCancelFragment
|
||||
import im.vector.app.features.crypto.verification.cancel.VerificationCancelDialogFragment
|
||||
import im.vector.app.features.crypto.verification.cancel.VerificationNotMeFragment
|
||||
import im.vector.app.features.crypto.verification.choose.VerificationChooseMethodFragment
|
||||
import im.vector.app.features.crypto.verification.conclusion.VerificationConclusionFragment
|
||||
@@ -633,6 +634,11 @@ interface FragmentModule {
|
||||
@FragmentKey(VerificationChooseMethodFragment::class)
|
||||
fun bindVerificationChooseMethodFragment(fragment: VerificationChooseMethodFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(VerificationCancelDialogFragment::class)
|
||||
fun bindVerificationCancelDialogFragment(fragment: VerificationCancelDialogFragment): DialogFragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(VerificationEmojiCodeFragment::class)
|
||||
@@ -653,11 +659,6 @@ interface FragmentModule {
|
||||
@FragmentKey(VerificationConclusionFragment::class)
|
||||
fun bindVerificationConclusionFragment(fragment: VerificationConclusionFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(VerificationCancelFragment::class)
|
||||
fun bindVerificationCancelFragment(fragment: VerificationCancelFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(QuadSLoadingFragment::class)
|
||||
|
@@ -32,6 +32,7 @@ import im.vector.app.features.crypto.keysbackup.settings.KeysBackupSettingsViewM
|
||||
import im.vector.app.features.crypto.quads.SharedSecureStorageViewModel
|
||||
import im.vector.app.features.crypto.recover.BootstrapSharedViewModel
|
||||
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
|
||||
import im.vector.app.features.crypto.verification.cancel.VerificationCancelViewModel
|
||||
import im.vector.app.features.crypto.verification.choose.VerificationChooseMethodViewModel
|
||||
import im.vector.app.features.crypto.verification.emoji.VerificationEmojiCodeViewModel
|
||||
import im.vector.app.features.devtools.RoomDevToolViewModel
|
||||
@@ -505,6 +506,11 @@ interface MavericksViewModelModule {
|
||||
@MavericksViewModelKey(VerificationChooseMethodViewModel::class)
|
||||
fun verificationChooseMethodViewModelFactory(factory: VerificationChooseMethodViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@MavericksViewModelKey(VerificationCancelViewModel::class)
|
||||
fun verificationCancelViewModelFactory(factory: VerificationCancelViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@MavericksViewModelKey(VerificationEmojiCodeViewModel::class)
|
||||
|
@@ -110,6 +110,12 @@ abstract class VectorBaseBottomSheetDialogFragment<VB : ViewBinding> : BottomShe
|
||||
resultListener?.onBottomSheetResult(bottomSheetResult, bottomSheetResultData)
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
override fun onCancel(dialog: DialogInterface) {
|
||||
super.onCancel(dialog)
|
||||
resultListener?.onBottomSheetResult(bottomSheetResult, bottomSheetResultData)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
_binding = getBinding(inflater, container)
|
||||
return views.root
|
||||
|
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.core.platform
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.airbnb.mvrx.MavericksView
|
||||
import dagger.hilt.android.EntryPointAccessors
|
||||
import im.vector.app.core.di.ActivityEntryPoint
|
||||
import im.vector.app.core.extensions.toMvRxBundle
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import reactivecircus.flowbinding.android.view.clicks
|
||||
|
||||
abstract class VectorBaseDialogFragment<VB : ViewBinding> : DialogFragment(), MavericksView {
|
||||
|
||||
/* ==========================================================================================
|
||||
* View
|
||||
* ========================================================================================== */
|
||||
|
||||
private var _binding: VB? = null
|
||||
|
||||
// This property is only valid between onCreateView and onDestroyView.
|
||||
protected val views: VB
|
||||
get() = _binding!!
|
||||
|
||||
abstract fun getBinding(inflater: LayoutInflater, container: ViewGroup?): VB
|
||||
|
||||
/* ==========================================================================================
|
||||
* View model
|
||||
* ========================================================================================== */
|
||||
|
||||
private lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
|
||||
protected val activityViewModelProvider
|
||||
get() = ViewModelProvider(requireActivity(), viewModelFactory)
|
||||
|
||||
protected val fragmentViewModelProvider
|
||||
get() = ViewModelProvider(this, viewModelFactory)
|
||||
|
||||
val vectorBaseActivity: VectorBaseActivity<*> by lazy {
|
||||
activity as VectorBaseActivity<*>
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
_binding = getBinding(inflater, container)
|
||||
return views.root
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return super.onCreateDialog(savedInstanceState).apply {
|
||||
window?.setLayout(ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.MATCH_PARENT)
|
||||
}
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
override fun onDestroyView() {
|
||||
_binding = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
val activityEntryPoint = EntryPointAccessors.fromActivity(vectorBaseActivity, ActivityEntryPoint::class.java)
|
||||
viewModelFactory = activityEntryPoint.viewModelFactory()
|
||||
super.onAttach(context)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
postInvalidate()
|
||||
}
|
||||
|
||||
protected fun setArguments(args: Parcelable? = null) {
|
||||
arguments = args.toMvRxBundle()
|
||||
}
|
||||
|
||||
/* ==========================================================================================
|
||||
* Views
|
||||
* ========================================================================================== */
|
||||
|
||||
protected fun View.debouncedClicks(onClicked: () -> Unit) {
|
||||
clicks()
|
||||
.onEach { onClicked() }
|
||||
.launchIn(viewLifecycleOwner.lifecycleScope)
|
||||
}
|
||||
|
||||
/* ==========================================================================================
|
||||
* ViewEvents
|
||||
* ========================================================================================== */
|
||||
|
||||
protected fun <T : VectorViewEvents> VectorViewModel<*, *, T>.observeViewEvents(observer: (T) -> Unit) {
|
||||
viewEvents
|
||||
.stream()
|
||||
.onEach {
|
||||
observer(it)
|
||||
}
|
||||
.launchIn(viewLifecycleOwner.lifecycleScope)
|
||||
}
|
||||
}
|
@@ -17,12 +17,14 @@ package im.vector.app.features.crypto.verification
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
@@ -37,7 +39,7 @@ import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
|
||||
import im.vector.app.databinding.BottomSheetVerificationBinding
|
||||
import im.vector.app.features.crypto.quads.SharedSecureStorageActivity
|
||||
import im.vector.app.features.crypto.verification.cancel.VerificationCancelFragment
|
||||
import im.vector.app.features.crypto.verification.cancel.VerificationCancelDialogFragment
|
||||
import im.vector.app.features.crypto.verification.cancel.VerificationNotMeFragment
|
||||
import im.vector.app.features.crypto.verification.choose.VerificationChooseMethodFragment
|
||||
import im.vector.app.features.crypto.verification.conclusion.VerificationConclusionFragment
|
||||
@@ -71,7 +73,8 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetV
|
||||
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
|
||||
val selfVerificationMode: Boolean = false,
|
||||
val userTrustLevel: RoomEncryptionTrustLevel? = null
|
||||
) : Parcelable
|
||||
|
||||
override val showExpanded = true
|
||||
@@ -85,10 +88,6 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetV
|
||||
return BottomSheetVerificationBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
init {
|
||||
isCancelable = false
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
@@ -135,6 +134,15 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetV
|
||||
}
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
override fun onCancel(dialog: DialogInterface) {
|
||||
viewModel.queryCancel()
|
||||
withState(viewModel) { state ->
|
||||
showCancelVerificationDialog(state)
|
||||
}
|
||||
super.onCancel(dialog)
|
||||
}
|
||||
|
||||
private val secretStartForActivityResult = registerStartForActivityResult { activityResult ->
|
||||
if (activityResult.resultCode == Activity.RESULT_OK) {
|
||||
val result = activityResult.data?.getStringExtra(SharedSecureStorageActivity.EXTRA_DATA_RESULT)
|
||||
@@ -196,7 +204,8 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetV
|
||||
|
||||
if (state.userWantsToCancel) {
|
||||
views.otherUserNameText.text = getString(R.string.are_you_sure)
|
||||
showFragment(VerificationCancelFragment::class)
|
||||
dismiss()
|
||||
showCancelVerificationDialog(state)
|
||||
return@withState
|
||||
}
|
||||
|
||||
@@ -357,6 +366,15 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetV
|
||||
}
|
||||
}
|
||||
|
||||
private fun showCancelVerificationDialog(state: VerificationBottomSheetViewState) {
|
||||
VerificationCancelDialogFragment.newInstance(
|
||||
VerificationArgs(
|
||||
state.otherUserMxItem?.id ?: "",
|
||||
state.pendingRequest.invoke()?.transactionId ?: state.transactionId,
|
||||
userTrustLevel = state.userTrustLevel)
|
||||
).show(vectorBaseActivity.supportFragmentManager, VerificationCancelDialogFragment::class.java.name)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun withArgs(roomId: String?, otherUserId: String, transactionId: String? = null): VerificationBottomSheet {
|
||||
return VerificationBottomSheet().apply {
|
||||
|
@@ -34,6 +34,7 @@ import im.vector.app.core.resources.StringProvider
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
|
||||
@@ -79,7 +80,8 @@ data class VerificationBottomSheetViewState(
|
||||
val userThinkItsNotHim: Boolean = false,
|
||||
val quadSContainsSecrets: Boolean = true,
|
||||
val quadSHasBeenReset: Boolean = false,
|
||||
val hasAnyOtherSession: Boolean = false
|
||||
val hasAnyOtherSession: Boolean = false,
|
||||
val userTrustLevel: RoomEncryptionTrustLevel? = null
|
||||
) : MavericksState {
|
||||
|
||||
constructor(args: VerificationBottomSheet.VerificationArgs) : this(
|
||||
@@ -140,6 +142,8 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
|
||||
it.deviceId != session.sessionParams.deviceId
|
||||
}
|
||||
|
||||
val userTrustLevel = trustLevel(initialState, sasTx, qrTx)
|
||||
|
||||
setState {
|
||||
copy(
|
||||
otherUserMxItem = userItem?.toMatrixItem(),
|
||||
@@ -150,7 +154,8 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
|
||||
isMe = initialState.otherUserId == session.myUserId,
|
||||
currentDeviceCanCrossSign = session.cryptoService().crossSigningService().canCrossSign(),
|
||||
quadSContainsSecrets = session.sharedSecretStorageService.isRecoverySetup(),
|
||||
hasAnyOtherSession = hasAnyOtherSession
|
||||
hasAnyOtherSession = hasAnyOtherSession,
|
||||
userTrustLevel = userTrustLevel
|
||||
)
|
||||
}
|
||||
|
||||
@@ -165,6 +170,23 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun trustLevel(
|
||||
state: VerificationBottomSheetViewState,
|
||||
sas: SasVerificationTransaction?,
|
||||
qr: QrCodeVerificationTransaction?): RoomEncryptionTrustLevel {
|
||||
val userTrustLevel = if (state.isMe) {
|
||||
if (sas?.state == VerificationTxState.Verified ||
|
||||
qr?.state == VerificationTxState.Verified ||
|
||||
state.verifiedFromPrivateKeys) RoomEncryptionTrustLevel.Trusted
|
||||
else RoomEncryptionTrustLevel.Warning
|
||||
} else {
|
||||
if (sas?.state == VerificationTxState.Verified || qr?.state == VerificationTxState.Verified) {
|
||||
RoomEncryptionTrustLevel.Trusted
|
||||
} else RoomEncryptionTrustLevel.Warning
|
||||
}
|
||||
return userTrustLevel
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
session.cryptoService().verificationService().removeListener(this)
|
||||
super.onCleared()
|
||||
|
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.crypto.verification.cancel
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.platform.VectorBaseDialogFragment
|
||||
import im.vector.app.databinding.DialogVerificationCancelBinding
|
||||
import im.vector.app.features.crypto.verification.VerificationBottomSheet
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class VerificationCancelDialogFragment : VectorBaseDialogFragment<DialogVerificationCancelBinding>() {
|
||||
|
||||
companion object {
|
||||
fun newInstance(args: VerificationBottomSheet.VerificationArgs): VerificationCancelDialogFragment {
|
||||
return VerificationCancelDialogFragment().apply {
|
||||
setArguments(args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
isCancelable = false
|
||||
}
|
||||
|
||||
@Inject
|
||||
lateinit var avatarRenderer: AvatarRenderer
|
||||
|
||||
private val viewModel by fragmentViewModel(VerificationCancelViewModel::class)
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): DialogVerificationCancelBinding {
|
||||
return DialogVerificationCancelBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
handleSkipAction()
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
state.userMxItem?.let { matrixItem ->
|
||||
|
||||
avatarRenderer.render(matrixItem, views.otherUserAvatarImageView)
|
||||
views.otherUserShield.render(state.userTrustLevel)
|
||||
|
||||
updateDialogContent(state, matrixItem)
|
||||
handleContinueAction(state)
|
||||
}
|
||||
|
||||
return@withState
|
||||
}
|
||||
|
||||
private fun handleSkipAction() {
|
||||
views.btnSkipAction.setOnClickListener {
|
||||
viewModel.confirmCancel()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleContinueAction(state: VerificationCancelViewState) {
|
||||
views.btnContinueAction.setOnClickListener {
|
||||
VerificationBottomSheet.withArgs(
|
||||
roomId = state.roomId,
|
||||
otherUserId = state.otherUserId,
|
||||
transactionId = state.transactionId
|
||||
).show(parentFragmentManager, "REQ")
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateDialogContent(state: VerificationCancelViewState, matrixItem: MatrixItem) {
|
||||
if (state.isMe) {
|
||||
if (state.currentDeviceCanCrossSign) {
|
||||
views.dialogContent.text = getString(R.string.verify_cancel_self_verification_from_trusted)
|
||||
} else {
|
||||
views.dialogContent.text = getString(R.string.verify_cancel_self_verification_from_untrusted)
|
||||
}
|
||||
} else {
|
||||
views.dialogContent.text = getString(R.string.verify_cancel_other, matrixItem.displayName, matrixItem.id)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.crypto.verification.cancel
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.airbnb.mvrx.parentFragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.app.core.extensions.cleanup
|
||||
import im.vector.app.core.extensions.configureWith
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
|
||||
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerificationCancelFragment @Inject constructor(
|
||||
val controller: VerificationCancelController
|
||||
) : VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(),
|
||||
VerificationCancelController.Listener {
|
||||
|
||||
private val viewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
|
||||
return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setupRecyclerView()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
views.bottomSheetVerificationRecyclerView.cleanup()
|
||||
controller.listener = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
views.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,104 @@
|
||||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.crypto.verification.cancel
|
||||
|
||||
import com.airbnb.mvrx.MavericksState
|
||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import dagger.hilt.EntryPoints
|
||||
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||
import im.vector.app.core.di.SingletonEntryPoint
|
||||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||
import im.vector.app.core.platform.EmptyAction
|
||||
import im.vector.app.core.platform.EmptyViewEvents
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.features.crypto.verification.VerificationBottomSheet
|
||||
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
|
||||
data class VerificationCancelViewState(
|
||||
val userMxItem: MatrixItem? = null,
|
||||
val otherUserId: String,
|
||||
val transactionId: String? = null,
|
||||
val roomId: String? = null,
|
||||
val userTrustLevel: RoomEncryptionTrustLevel? = null,
|
||||
val isMe: Boolean = false,
|
||||
val currentDeviceCanCrossSign: Boolean = false,
|
||||
) : MavericksState
|
||||
|
||||
class VerificationCancelViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: VerificationCancelViewState,
|
||||
private val session: Session
|
||||
) : VectorViewModel<VerificationCancelViewState, EmptyAction, EmptyViewEvents>(initialState), VerificationService.Listener {
|
||||
|
||||
init {
|
||||
session.cryptoService().verificationService().addListener(this)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
session.cryptoService().verificationService().removeListener(this)
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : MavericksAssistedViewModelFactory<VerificationCancelViewModel, VerificationCancelViewState> {
|
||||
override fun create(initialState: VerificationCancelViewState): VerificationCancelViewModel
|
||||
}
|
||||
|
||||
companion object : MavericksViewModelFactory<VerificationCancelViewModel, VerificationCancelViewState> by hiltMavericksViewModelFactory() {
|
||||
|
||||
override fun initialState(viewModelContext: ViewModelContext): VerificationCancelViewState {
|
||||
val args = viewModelContext.args<VerificationBottomSheet.VerificationArgs>()
|
||||
val session = EntryPoints.get(viewModelContext.app(), SingletonEntryPoint::class.java).activeSessionHolder().getActiveSession()
|
||||
val matrixItem = session.getUser(args.otherUserId)?.toMatrixItem()
|
||||
|
||||
return VerificationCancelViewState(
|
||||
userMxItem = matrixItem,
|
||||
otherUserId = args.otherUserId,
|
||||
roomId = args.roomId,
|
||||
transactionId = args.verificationId,
|
||||
userTrustLevel = args.userTrustLevel,
|
||||
isMe = args.otherUserId == session.myUserId,
|
||||
currentDeviceCanCrossSign = session.cryptoService().crossSigningService().canCrossSign()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun handle(action: EmptyAction) {}
|
||||
|
||||
private fun cancelAllPendingVerifications(state: VerificationCancelViewState) {
|
||||
session.cryptoService()
|
||||
.verificationService().getExistingVerificationRequest(state.userMxItem?.id ?: "", state.transactionId)?.let {
|
||||
session.cryptoService().verificationService().cancelVerificationRequest(it)
|
||||
}
|
||||
session.cryptoService()
|
||||
.verificationService()
|
||||
.getExistingTransaction(state.userMxItem?.id ?: "", state.transactionId ?: "")
|
||||
?.cancel(CancelCode.User)
|
||||
}
|
||||
|
||||
fun confirmCancel() = withState { state ->
|
||||
cancelAllPendingVerifications(state)
|
||||
}
|
||||
}
|
90
vector/src/main/res/layout/dialog_verification_cancel.xml
Normal file
90
vector/src/main/res/layout/dialog_verification_cancel.xml
Normal file
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:background="?colorSurface"
|
||||
android:orientation="vertical"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/otherUserAvatarImageView"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@sample/user_round_avatars" />
|
||||
|
||||
<im.vector.app.core.ui.views.ShieldImageView
|
||||
android:id="@+id/otherUserShield"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
app:layout_constraintCircle="@id/otherUserAvatarImageView"
|
||||
app:layout_constraintCircleAngle="139"
|
||||
app:layout_constraintCircleRadius="20dp"
|
||||
tools:ignore="MissingConstraints" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/Widget.Vector.TextView.HeadlineMedium"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:text="@string/are_you_sure"
|
||||
android:textColor="?vctr_content_primary"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@id/otherUserAvatarImageView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/otherUserAvatarImageView"
|
||||
app:layout_constraintTop_toTopOf="@id/otherUserAvatarImageView"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/dialog_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="18dp"
|
||||
android:layout_marginStart="20dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/otherUserAvatarImageView"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:textColor="?vctr_content_secondary"
|
||||
style="@style/TextAppearance.Vector.Body"
|
||||
tools:text="@string/verify_cancel_self_verification_from_untrusted"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_skip_action"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?colorError"
|
||||
android:layout_marginTop="18dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:padding="0dp"
|
||||
android:background="@android:color/transparent"
|
||||
app:layout_constraintEnd_toStartOf="@id/btn_continue_action"
|
||||
app:layout_constraintTop_toBottomOf="@id/dialog_content"
|
||||
android:text="@string/action_skip"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_continue_action"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?colorPrimary"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:padding="0dp"
|
||||
app:layout_constraintTop_toTopOf="@id/btn_skip_action"
|
||||
android:background="@android:color/transparent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/dialog_content"
|
||||
android:text="@string/_continue"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@@ -3022,8 +3022,8 @@
|
||||
<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_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_cancel_other">You won’t verify %1$s (%2$s) if you cancel now. Start again in their user profile.</string>
|
||||
|
||||
<string name="verify_not_me_self_verification">
|
||||
|
Reference in New Issue
Block a user