Verification: migrate to Epoxy - Conclusion
This commit is contained in:
parent
7170471686
commit
cd1665a8e8
|
@ -21,6 +21,7 @@ import android.view.View
|
|||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.transition.AutoTransition
|
||||
|
@ -72,6 +73,9 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
@BindView(R.id.verificationRequestName)
|
||||
lateinit var otherUserNameText: TextView
|
||||
|
||||
@BindView(R.id.verificationRequestShield)
|
||||
lateinit var otherUserShield: View
|
||||
|
||||
@BindView(R.id.verificationRequestAvatar)
|
||||
lateinit var otherUserAvatarImageView: ImageView
|
||||
|
||||
|
@ -95,8 +99,15 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||
|
||||
override fun invalidate() = withState(viewModel) {
|
||||
it.otherUserMxItem?.let { matrixItem ->
|
||||
otherUserNameText.text = getString(R.string.verification_request_alert_title, matrixItem.getBestName())
|
||||
avatarRenderer.render(matrixItem, otherUserAvatarImageView)
|
||||
|
||||
if(it.sasTransactionState == SasVerificationTxState.Verified) {
|
||||
otherUserNameText.text = getString(R.string.verification_verified_user, matrixItem.getBestName())
|
||||
otherUserShield.isVisible = true
|
||||
} else {
|
||||
otherUserNameText.text = getString(R.string.verification_verify_user, matrixItem.getBestName())
|
||||
otherUserShield.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
// Did the request result in a SAS transaction?
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.crypto.verification.conclusion
|
||||
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.bottomsheet.bottomSheetSeparatorItem
|
||||
import im.vector.riotx.core.resources.ColorProvider
|
||||
import im.vector.riotx.core.resources.StringProvider
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem
|
||||
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
|
||||
import im.vector.riotx.features.html.EventHtmlRenderer
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerificationConclusionController @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val colorProvider: ColorProvider,
|
||||
private val eventHtmlRenderer: EventHtmlRenderer
|
||||
) : EpoxyController() {
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
private var viewState: VerificationConclusionViewState? = null
|
||||
|
||||
init {
|
||||
// We are requesting a model build directly as the first build of epoxy is on the main thread.
|
||||
// It avoids to build the whole list on the main thread.
|
||||
requestModelBuild()
|
||||
}
|
||||
|
||||
fun update(viewState: VerificationConclusionViewState) {
|
||||
this.viewState = viewState
|
||||
requestModelBuild()
|
||||
}
|
||||
|
||||
override fun buildModels() {
|
||||
val state = viewState ?: return
|
||||
|
||||
when (state.conclusionState) {
|
||||
ConclusionState.SUCCESS -> {
|
||||
bottomSheetVerificationNoticeItem {
|
||||
id("notice")
|
||||
notice(stringProvider.getString(R.string.verification_conclusion_ok_notice))
|
||||
}
|
||||
|
||||
bottomSheetVerificationBigImageItem {
|
||||
id("image")
|
||||
imageRes(R.drawable.ic_shield_trusted)
|
||||
}
|
||||
}
|
||||
ConclusionState.WARNING -> {
|
||||
bottomSheetVerificationNoticeItem {
|
||||
id("notice")
|
||||
notice(stringProvider.getString(R.string.verification_conclusion_not_secure))
|
||||
}
|
||||
|
||||
bottomSheetVerificationBigImageItem {
|
||||
id("image")
|
||||
imageRes(R.drawable.ic_shield_warning)
|
||||
}
|
||||
|
||||
bottomSheetVerificationNoticeItem {
|
||||
id("warning_notice")
|
||||
notice(eventHtmlRenderer.render(stringProvider.getString(R.string.verification_conclusion_compromised)))
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
bottomSheetSeparatorItem {
|
||||
id("sep0")
|
||||
}
|
||||
|
||||
bottomSheetVerificationActionItem {
|
||||
id("done")
|
||||
title(stringProvider.getString(R.string.done))
|
||||
titleColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
|
||||
iconRes(R.drawable.ic_arrow_right)
|
||||
iconColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
|
||||
listener { listener?.onButtonTapped() }
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onButtonTapped()
|
||||
}
|
||||
}
|
|
@ -15,24 +15,25 @@
|
|||
*/
|
||||
package im.vector.riotx.features.crypto.verification.conclusion
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import butterknife.OnClick
|
||||
import android.view.View
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.parentFragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.extensions.setTextOrHide
|
||||
import im.vector.riotx.core.extensions.cleanup
|
||||
import im.vector.riotx.core.extensions.configureWith
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.features.crypto.verification.VerificationAction
|
||||
import im.vector.riotx.features.crypto.verification.VerificationBottomSheetViewModel
|
||||
import io.noties.markwon.Markwon
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.android.synthetic.main.fragment_verification_conclusion.*
|
||||
import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class VerificationConclusionFragment @Inject constructor() : VectorBaseFragment() {
|
||||
class VerificationConclusionFragment @Inject constructor(
|
||||
val controller: VerificationConclusionController
|
||||
) : VectorBaseFragment(), VerificationConclusionController.Listener {
|
||||
|
||||
@Parcelize
|
||||
data class Args(
|
||||
|
@ -40,38 +41,39 @@ class VerificationConclusionFragment @Inject constructor() : VectorBaseFragment(
|
|||
val cancelReason: String?
|
||||
) : Parcelable
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_verification_conclusion
|
||||
|
||||
private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
|
||||
|
||||
private val viewModel by fragmentViewModel(VerificationConclusionViewModel::class)
|
||||
|
||||
override fun invalidate() = withState(viewModel) {
|
||||
when (it.conclusionState) {
|
||||
ConclusionState.SUCCESS -> {
|
||||
verificationConclusionTitle.text = getString(R.string.sas_verified)
|
||||
verifyConclusionDescription.setTextOrHide(getString(R.string.sas_verified_successful_description))
|
||||
verifyConclusionBottomDescription.text = getString(R.string.verification_green_shield)
|
||||
verifyConclusionImageView.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_shield_trusted))
|
||||
}
|
||||
ConclusionState.WARNING -> {
|
||||
verificationConclusionTitle.text = getString(R.string.verification_conclusion_not_secure)
|
||||
verifyConclusionDescription.isVisible = false
|
||||
verifyConclusionImageView.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_shield_warning))
|
||||
override fun getLayoutResId() = R.layout.bottom_sheet_verification_child_fragment
|
||||
|
||||
verifyConclusionBottomDescription.text = Markwon.builder(requireContext())
|
||||
.build()
|
||||
.toMarkdown(getString(R.string.verification_conclusion_compromised))
|
||||
}
|
||||
ConclusionState.CANCELLED -> {
|
||||
// Just dismiss in this case
|
||||
sharedViewModel.handle(VerificationAction.GotItConclusion)
|
||||
}
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setupRecyclerView()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
bottomSheetVerificationRecyclerView.cleanup()
|
||||
controller.listener = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false)
|
||||
controller.listener = this
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
if (state.conclusionState == ConclusionState.CANCELLED) {
|
||||
// Just dismiss in this case
|
||||
sharedViewModel.handle(VerificationAction.GotItConclusion)
|
||||
} else {
|
||||
controller.update(state)
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.verificationConclusionButton)
|
||||
fun onButtonTapped() {
|
||||
override fun onButtonTapped() {
|
||||
sharedViewModel.handle(VerificationAction.GotItConclusion)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import im.vector.matrix.android.api.session.crypto.sas.safeValueOf
|
|||
import im.vector.riotx.core.platform.EmptyAction
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
|
||||
data class SASVerificationConclusionViewState(
|
||||
data class VerificationConclusionViewState(
|
||||
val conclusionState: ConclusionState = ConclusionState.CANCELLED
|
||||
) : MvRxState
|
||||
|
||||
|
@ -33,22 +33,22 @@ enum class ConclusionState {
|
|||
CANCELLED
|
||||
}
|
||||
|
||||
class VerificationConclusionViewModel(initialState: SASVerificationConclusionViewState)
|
||||
: VectorViewModel<SASVerificationConclusionViewState, EmptyAction>(initialState) {
|
||||
class VerificationConclusionViewModel(initialState: VerificationConclusionViewState)
|
||||
: VectorViewModel<VerificationConclusionViewState, EmptyAction>(initialState) {
|
||||
|
||||
companion object : MvRxViewModelFactory<VerificationConclusionViewModel, SASVerificationConclusionViewState> {
|
||||
companion object : MvRxViewModelFactory<VerificationConclusionViewModel, VerificationConclusionViewState> {
|
||||
|
||||
override fun initialState(viewModelContext: ViewModelContext): SASVerificationConclusionViewState? {
|
||||
override fun initialState(viewModelContext: ViewModelContext): VerificationConclusionViewState? {
|
||||
val args = viewModelContext.args<VerificationConclusionFragment.Args>()
|
||||
|
||||
return when (safeValueOf(args.cancelReason)) {
|
||||
CancelCode.MismatchedSas,
|
||||
CancelCode.MismatchedCommitment,
|
||||
CancelCode.MismatchedKeys -> {
|
||||
SASVerificationConclusionViewState(ConclusionState.WARNING)
|
||||
VerificationConclusionViewState(ConclusionState.WARNING)
|
||||
}
|
||||
else -> {
|
||||
SASVerificationConclusionViewState(
|
||||
VerificationConclusionViewState(
|
||||
if (args.isSuccessFull) ConclusionState.SUCCESS
|
||||
else ConclusionState.CANCELLED
|
||||
)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<androidx.core.widget.NestedScrollView 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:id="@+id/bottomSheetScrollView"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -9,47 +10,62 @@
|
|||
android:fadeScrollbars="false"
|
||||
android:scrollbars="vertical">
|
||||
|
||||
<LinearLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
<ImageView
|
||||
android:id="@+id/verificationRequestAvatar"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:background="@drawable/circle"
|
||||
android:contentDescription="@string/avatar"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/verificationRequestShield"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/ic_shield_trusted"
|
||||
app:layout_constraintCircle="@+id/verificationRequestAvatar"
|
||||
app:layout_constraintCircleAngle="135"
|
||||
app:layout_constraintCircleRadius="16dp"
|
||||
tools:ignore="MissingConstraints" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/verificationRequestName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/verificationRequestAvatar"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:background="@drawable/circle"
|
||||
android:contentDescription="@string/avatar"
|
||||
android:scaleType="centerCrop"
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/verificationRequestName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/verification_request_alert_title"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/verificationRequestAvatar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/verificationRequestAvatar"
|
||||
app:layout_constraintTop_toTopOf="@+id/verificationRequestAvatar"
|
||||
tools:text="@string/verification_verify_user" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/bottomSheetFragmentContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp" />
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/verificationRequestAvatar" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
|
|
@ -46,7 +46,8 @@
|
|||
|
||||
<string name="aria_qr_code_description">QR code image</string>
|
||||
|
||||
<string name="verification_request_alert_title">Verify %s</string>
|
||||
<string name="verification_verify_user">Verify %s</string>
|
||||
<string name="verification_verified_user">Verified %s</string>
|
||||
<string name="verification_request_waiting_for">Waiting for %s…</string>
|
||||
<string name="verification_request_alert_description">For extra security, verify %s by checking a one-time code on both your devices.\n\nFor maximum security, do this in person.</string>
|
||||
<string name="room_profile_not_encrypted_subtitle">Messages in this room are not end-to-end encrypted.</string>
|
||||
|
@ -90,6 +91,7 @@
|
|||
<string name="verification_request_start_notice">For maximum security, do this in person.</string>
|
||||
|
||||
<string name="verification_emoji_notice">Compare the unique emoji, ensuring they appear in the same order.</string>
|
||||
<string name="verification_code_notice">Compare the code with the one displayed on the the other user\'s screen.</string>
|
||||
<string name="verification_code_notice">Compare the code with the one displayed on the other user\'s screen.</string>
|
||||
<string name="verification_conclusion_ok_notice">Messages with this user are end-to-end encrypted and can\'t be read by third parties.</string>
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue