Merge pull request #2819 from vector-im/feature/bma/lang

a11y
This commit is contained in:
Benoit Marty 2021-02-16 11:41:02 +01:00 committed by GitHub
commit 03735d9213
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
221 changed files with 651 additions and 607 deletions

View File

@ -8,6 +8,7 @@ Improvements 🙌:
- VoIP : new tiles in timeline
- Improve room profile UX
- Upgrade Jitsi library from 2.9.3 to 3.1.0
- a11y improvements
Bugfix 🐛:
- VoIP : fix audio devices output

View File

@ -17,6 +17,10 @@
<issue id="ButtonOrder" severity="error" />
<issue id="TextFields" severity="error" />
<!-- Accessibility -->
<issue id="LabelFor" severity="error" />
<issue id="ContentDescription" severity="error" />
<!-- Layout -->
<issue id="UnknownIdInLayout" severity="error" />
<issue id="StringFormatCount" severity="error" />

View File

@ -159,6 +159,7 @@
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:importantForAccessibility="no"
android:src="@drawable/ic_settings_x" />
</LinearLayout>

View File

@ -21,6 +21,7 @@
div {
padding: 4px;
}
</style>
</head>
<body>
@ -388,7 +389,7 @@ SOFTWARE.
<li>
<b>dialogs / android-dialer</b>
<br/>
Copyright (c) 2017-present, dialog LLC <info@dlg.im>
Copyright (c) 2017-present, dialog LLC &lt;info@dlg.im&gt;
</li>
</ul>
<pre>
@ -570,20 +571,24 @@ Apache License
<pre>
CC-BY 4.0
</pre>
<ul>
<li>
<b>Twitter/twemoji Graphics</b>
<br/>
</li>
</pre>
</ul>
<pre>
ISC License
</pre>
<ul>
<li>
<b>DanielMartinus / Konfetti</b>
<br/>
Copyright (c) 2017 Dion Segijn
</li>
</pre>
</ul>
</body>
</html>

View File

@ -23,7 +23,6 @@ import im.vector.app.features.crypto.keysrequest.KeyRequestHandler
import im.vector.app.features.crypto.verification.IncomingVerificationRequestHandler
import im.vector.app.features.notifications.PushRuleTriggerListener
import im.vector.app.features.session.SessionListener
import org.matrix.android.sdk.api.auth.AuthenticationService
import org.matrix.android.sdk.api.session.Session
import timber.log.Timber
import java.util.concurrent.atomic.AtomicReference
@ -31,8 +30,7 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ActiveSessionHolder @Inject constructor(private val authenticationService: AuthenticationService,
private val sessionObservableStore: ActiveSessionDataSource,
class ActiveSessionHolder @Inject constructor(private val sessionObservableStore: ActiveSessionDataSource,
private val keyRequestHandler: KeyRequestHandler,
private val incomingVerificationRequestHandler: IncomingVerificationRequestHandler,
private val callManager: WebRtcCallManager,

View File

@ -61,7 +61,7 @@ class ExportKeysDialog {
passwordVisible = !passwordVisible
views.exportDialogEt.showPassword(passwordVisible)
views.exportDialogEtConfirm.showPassword(passwordVisible)
views.exportDialogShowPassword.setImageResource(if (passwordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
views.exportDialogShowPassword.render(passwordVisible)
}
val exportDialog = builder.show()

View File

@ -44,7 +44,7 @@ class PromptPasswordDialog {
views.promptPasswordPasswordReveal.setOnClickListener {
passwordVisible = !passwordVisible
views.promptPassword.showPassword(passwordVisible)
views.promptPasswordPasswordReveal.setImageResource(if (passwordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
views.promptPasswordPasswordReveal.render(passwordVisible)
}
AlertDialog.Builder(activity)

View File

@ -21,7 +21,6 @@ import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.features.crypto.util.toImageRes
import im.vector.app.features.home.AvatarRenderer
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.util.MatrixItem
@ -47,6 +46,6 @@ abstract class BaseProfileMatrixItem<T : ProfileMatrixItem.Holder> : VectorEpoxy
holder.subtitleView.setTextOrHide(matrixId)
holder.editableView.isVisible = editable
avatarRenderer.render(matrixItem, holder.avatarImageView)
holder.avatarDecorationImageView.setImageResource(userEncryptionTrustLevel.toImageRes())
holder.avatarDecorationImageView.render(userEncryptionTrustLevel)
}
}

View File

@ -23,6 +23,7 @@ import android.widget.TextView
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.ui.views.ShieldImageView
@EpoxyModelClass(layout = R.layout.item_profile_matrix_item)
abstract class ProfileMatrixItem : BaseProfileMatrixItem<ProfileMatrixItem.Holder>() {
@ -31,7 +32,7 @@ abstract class ProfileMatrixItem : BaseProfileMatrixItem<ProfileMatrixItem.Holde
val titleView by bind<TextView>(R.id.matrixItemTitle)
val subtitleView by bind<TextView>(R.id.matrixItemSubtitle)
val avatarImageView by bind<ImageView>(R.id.matrixItemAvatar)
val avatarDecorationImageView by bind<ImageView>(R.id.matrixItemAvatarDecoration)
val avatarDecorationImageView by bind<ShieldImageView>(R.id.matrixItemAvatarDecoration)
val editableView by bind<View>(R.id.matrixItemEditable)
}
}

View File

@ -19,17 +19,16 @@ package im.vector.app.core.extensions
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import im.vector.app.core.utils.EventObserver
import im.vector.app.core.utils.FirstThrottler
import im.vector.app.core.utils.LiveEvent
inline fun <T> LiveData<T>.observeK(owner: LifecycleOwner, crossinline observer: (T?) -> Unit) {
this.observe(owner, Observer { observer(it) })
this.observe(owner, { observer(it) })
}
inline fun <T> LiveData<T>.observeNotNull(owner: LifecycleOwner, crossinline observer: (T) -> Unit) {
this.observe(owner, Observer { it?.run(observer) })
this.observe(owner, { it?.run(observer) })
}
inline fun <T> LiveData<LiveEvent<T>>.observeEvent(owner: LifecycleOwner, crossinline observer: (T) -> Unit) {

View File

@ -40,7 +40,6 @@ import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentFactory
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.viewbinding.ViewBinding
import com.bumptech.glide.util.Util
@ -208,12 +207,12 @@ abstract class VectorBaseActivity<VB: ViewBinding> : AppCompatActivity(), HasScr
navigator = screenComponent.navigator()
activeSessionHolder = screenComponent.activeSessionHolder()
vectorPreferences = vectorComponent.vectorPreferences()
configurationViewModel.activityRestarter.observe(this, Observer {
configurationViewModel.activityRestarter.observe(this) {
if (!it.hasBeenHandled) {
// Recreate the Activity because configuration has changed
restart()
}
})
}
pinLocker.getLiveState().observeNotNull(this) {
if (this@VectorBaseActivity !is UnlockedActivity && it == PinLocker.State.LOCKED) {
navigator.openPinCode(this, pinStartForActivityResult, PinMode.AUTH)

View File

@ -89,7 +89,7 @@ class KnownCallsViewHolder {
this.pipWrapper = pipWrapper
this.currentCallsView?.callback = interactionListener
pipWrapper.setOnClickListener(
DebouncedClickListener({ _ ->
DebouncedClickListener({
interactionListener.onTapToReturnToCall()
})
)

View File

@ -56,6 +56,7 @@ class ReadReceiptsView @JvmOverloads constructor(
private fun setupView() {
inflate(context, R.layout.view_read_receipts, this)
contentDescription = context.getString(R.string.a11y_view_read_receipts)
}
fun render(readReceipts: List<ReadReceiptData>, avatarRenderer: AvatarRenderer, clickListener: OnClickListener) {

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2021 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.ui.views
import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView
import im.vector.app.R
class RevealPasswordImageView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr) {
init {
render(false)
}
fun render(isPasswordShown: Boolean) {
if (isPasswordShown) {
contentDescription = context.getString(R.string.a11y_hide_password)
setImageResource(R.drawable.ic_eye_closed)
} else {
contentDescription = context.getString(R.string.a11y_show_password)
setImageResource(R.drawable.ic_eye)
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2021 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.ui.views
import android.content.Context
import android.util.AttributeSet
import androidx.annotation.DrawableRes
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.view.isVisible
import im.vector.app.R
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
class ShieldImageView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr) {
init {
if (isInEditMode) {
render(RoomEncryptionTrustLevel.Trusted)
}
}
fun render(roomEncryptionTrustLevel: RoomEncryptionTrustLevel?) {
isVisible = roomEncryptionTrustLevel != null
when (roomEncryptionTrustLevel) {
RoomEncryptionTrustLevel.Default -> {
contentDescription = context.getString(R.string.a11y_trust_level_default)
setImageResource(R.drawable.ic_shield_black)
}
RoomEncryptionTrustLevel.Warning -> {
contentDescription = context.getString(R.string.a11y_trust_level_warning)
setImageResource(R.drawable.ic_shield_warning)
}
RoomEncryptionTrustLevel.Trusted -> {
contentDescription = context.getString(R.string.a11y_trust_level_trusted)
setImageResource(R.drawable.ic_shield_trusted)
}
}
}
}
@DrawableRes
fun RoomEncryptionTrustLevel.toDrawableRes(): Int {
return when (this) {
RoomEncryptionTrustLevel.Default -> R.drawable.ic_shield_black
RoomEncryptionTrustLevel.Warning -> R.drawable.ic_shield_warning
RoomEncryptionTrustLevel.Trusted -> R.drawable.ic_shield_trusted
}
}

View File

@ -27,8 +27,8 @@ class CountUpTimer(private val intervalInMs: Long) {
private val resumed: AtomicBoolean = AtomicBoolean(false)
private val disposable = Observable.interval(intervalInMs, TimeUnit.MILLISECONDS)
.filter { _ -> resumed.get() }
.doOnNext { _ -> elapsedTime.addAndGet(intervalInMs) }
.filter { resumed.get() }
.doOnNext { elapsedTime.addAndGet(intervalInMs) }
.subscribe {
tickListener?.onTick(elapsedTime.get())
}

View File

@ -49,7 +49,7 @@ open class BehaviorDataSource<T>(private val defaultValue: T? = null) : MutableD
private fun createRelay(): BehaviorRelay<T> {
return if (defaultValue == null) {
BehaviorRelay.create<T>()
BehaviorRelay.create()
} else {
BehaviorRelay.createDefault(defaultValue)
}

View File

@ -19,12 +19,11 @@ package im.vector.app.core.utils
import io.reactivex.Completable
import io.reactivex.Single
import io.reactivex.disposables.Disposable
import io.reactivex.functions.Consumer
import io.reactivex.internal.functions.Functions
import timber.log.Timber
fun <T> Single<T>.subscribeLogError(): Disposable {
return subscribe(Functions.emptyConsumer(), Consumer { Timber.e(it) })
return subscribe(Functions.emptyConsumer(), { Timber.e(it) })
}
fun Completable.subscribeLogError(): Disposable {

View File

@ -87,14 +87,7 @@ class PromptFragment : VectorBaseFragment<FragmentReauthConfirmBinding>() {
}
views.passwordField.showPassword(it.passwordVisible)
if (it.passwordVisible) {
views.passwordReveal.setImageResource(R.drawable.ic_eye_closed)
views.passwordReveal.contentDescription = getString(R.string.a11y_hide_password)
} else {
views.passwordReveal.setImageResource(R.drawable.ic_eye)
views.passwordReveal.contentDescription = getString(R.string.a11y_show_password)
}
views.passwordReveal.render(it.passwordVisible)
if (it.lastErrorCode != null) {
when (it.flowType) {

View File

@ -102,7 +102,7 @@ abstract class RecyclerViewPresenter<T>(context: Context?) : AutocompletePresent
return LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
}
private class Observer internal constructor(private val root: DataSetObserver) : RecyclerView.AdapterDataObserver() {
private class Observer constructor(private val root: DataSetObserver) : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
root.onChanged()
}

View File

@ -31,7 +31,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
class AutocompleteMemberPresenter @AssistedInject constructor(context: Context,
@Assisted val roomId: String,
private val session: Session,
session: Session,
private val controller: AutocompleteMemberController
) : RecyclerViewPresenter<RoomMemberSummary>(context), AutocompleteClickListener<RoomMemberSummary> {

View File

@ -383,7 +383,7 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
mode: String?): Intent {
return Intent(context, VectorCallActivity::class.java).apply {
// what could be the best flags?
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
flags = FLAG_ACTIVITY_CLEAR_TOP
putExtra(MvRx.KEY_ARG, CallArgs(roomId, callId, otherUserId, isIncomingCall, isVideoCall))
putExtra(EXTRA_MODE, mode)
}

View File

@ -19,7 +19,6 @@ import android.app.Activity
import android.content.Context
import android.content.Intent
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.Observer
import im.vector.app.R
import im.vector.app.core.extensions.addFragmentToBackstack
import im.vector.app.core.extensions.observeEvent
@ -54,7 +53,7 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() {
viewModel = viewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java)
viewModel.initSession(session)
viewModel.keySourceModel.observe(this, Observer { keySource ->
viewModel.keySourceModel.observe(this) { keySource ->
if (keySource != null && !keySource.isInQuadS && supportFragmentManager.fragments.isEmpty()) {
val isBackupCreatedFromPassphrase =
viewModel.keyVersionResult.value?.getAuthDataAsMegolmBackupAuthData()?.privateKeySalt != null
@ -64,7 +63,7 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() {
replaceFragment(R.id.container, KeysBackupRestoreFromKeyFragment::class.java)
}
}
})
}
viewModel.keyVersionResultError.observeEvent(this) { message ->
AlertDialog.Builder(this)
@ -111,9 +110,9 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() {
}
}
viewModel.loadingEvent.observe(this, Observer {
viewModel.loadingEvent.observe(this) {
updateWaitingView(it)
})
}
viewModel.importRoomKeysFinishWithResult.observeEvent(this) {
// set data?

View File

@ -22,7 +22,6 @@ import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.core.widget.doOnTextChanged
import androidx.lifecycle.Observer
import im.vector.app.R
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.VectorBaseFragment
@ -56,9 +55,9 @@ class KeysBackupRestoreFromKeyFragment @Inject constructor()
}
views.keyInputLayout.error = viewModel.recoveryCodeErrorText.value
viewModel.recoveryCodeErrorText.observe(viewLifecycleOwner, Observer { newValue ->
viewModel.recoveryCodeErrorText.observe(viewLifecycleOwner) { newValue ->
views.keyInputLayout.error = newValue
})
}
views.keysRestoreButton.setOnClickListener { onRestoreFromKey() }
views.keysBackupImport.setOnClickListener { onImport() }

View File

@ -24,7 +24,6 @@ import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.core.text.set
import androidx.core.widget.doOnTextChanged
import androidx.lifecycle.Observer
import im.vector.app.R
import im.vector.app.core.extensions.showPassword
import im.vector.app.core.platform.VectorBaseFragment
@ -51,17 +50,17 @@ class KeysBackupRestoreFromPassphraseFragment @Inject constructor() : VectorBase
viewModel = fragmentViewModelProvider.get(KeysBackupRestoreFromPassphraseViewModel::class.java)
sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java)
viewModel.passphraseErrorText.observe(viewLifecycleOwner, Observer { newValue ->
viewModel.passphraseErrorText.observe(viewLifecycleOwner) { newValue ->
views.keysBackupPassphraseEnterTil.error = newValue
})
}
views.helperTextWithLink.text = spannableStringForHelperText()
viewModel.showPasswordMode.observe(viewLifecycleOwner, Observer {
viewModel.showPasswordMode.observe(viewLifecycleOwner) {
val shouldBeVisible = it ?: false
views.keysBackupPassphraseEnterEdittext.showPassword(shouldBeVisible)
views.keysBackupViewShowPassword.setImageResource(if (shouldBeVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
})
views.keysBackupViewShowPassword.render(shouldBeVisible)
}
views.keysBackupPassphraseEnterEdittext.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) {

View File

@ -21,7 +21,6 @@ import android.content.Intent
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Observer
import im.vector.app.R
import im.vector.app.core.dialogs.ExportKeysDialog
import im.vector.app.core.extensions.observeEvent
@ -49,20 +48,20 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
viewModel.showManualExport.value = intent.getBooleanExtra(EXTRA_SHOW_MANUAL_EXPORT, false)
viewModel.initSession(session)
viewModel.isCreatingBackupVersion.observe(this, Observer {
viewModel.isCreatingBackupVersion.observe(this) {
val isCreating = it ?: false
if (isCreating) {
showWaitingView()
} else {
hideWaitingView()
}
})
}
viewModel.loadingStatus.observe(this, Observer {
viewModel.loadingStatus.observe(this) {
it?.let {
updateWaitingView(it)
}
})
}
viewModel.navigateEvent.observeEvent(this) { uxStateEvent ->
when (uxStateEvent) {
@ -99,7 +98,7 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
}
}
viewModel.prepareRecoverFailError.observe(this, Observer { error ->
viewModel.prepareRecoverFailError.observe(this) { error ->
if (error != null) {
AlertDialog.Builder(this)
.setTitle(R.string.unknown_error)
@ -110,9 +109,9 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
}
.show()
}
})
}
viewModel.creatingBackupError.observe(this, Observer { error ->
viewModel.creatingBackupError.observe(this) { error ->
if (error != null) {
AlertDialog.Builder(this)
.setTitle(R.string.unexpected_error)
@ -123,7 +122,7 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
}
.show()
}
})
}
}
private val saveStartForActivityResult = registerStartForActivityResult { activityResult ->

View File

@ -20,7 +20,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.Observer
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.LiveEvent
import im.vector.app.databinding.FragmentKeysBackupSetupStep1Binding
@ -40,12 +39,12 @@ class KeysBackupSetupStep1Fragment @Inject constructor() : VectorBaseFragment<Fr
viewModel = activityViewModelProvider.get(KeysBackupSetupSharedViewModel::class.java)
viewModel.showManualExport.observe(viewLifecycleOwner, Observer {
viewModel.showManualExport.observe(viewLifecycleOwner) {
val showOption = it ?: false
// Can't use isVisible because the kotlin compiler will crash with Back-end (JVM) Internal error: wrong code generated
views.keysBackupSetupStep1AdvancedOptionText.visibility = if (showOption) View.VISIBLE else View.GONE
views.keysBackupSetupStep1ManualExportButton.visibility = if (showOption) View.VISIBLE else View.GONE
})
}
views.keysBackupSetupStep1Button.setOnClickListener { onButtonClick() }
views.keysBackupSetupStep1ManualExportButton.setOnClickListener { onManualExportClick() }

View File

@ -21,7 +21,6 @@ import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import androidx.core.widget.doOnTextChanged
import androidx.lifecycle.Observer
import androidx.lifecycle.viewModelScope
import androidx.transition.TransitionManager
import com.nulabinc.zxcvbn.Zxcvbn
@ -30,7 +29,6 @@ import im.vector.app.core.extensions.showPassword
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentKeysBackupSetupStep2Binding
import im.vector.app.features.settings.VectorLocale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -69,7 +67,7 @@ class KeysBackupSetupStep2Fragment @Inject constructor() : VectorBaseFragment<Fr
* ========================================================================================== */
private fun bindViewToViewModel() {
viewModel.passwordStrength.observe(viewLifecycleOwner, Observer { strength ->
viewModel.passwordStrength.observe(viewLifecycleOwner) { strength ->
if (strength == null) {
views.keysBackupSetupStep2PassphraseStrengthLevel.strength = 0
views.keysBackupSetupStep2PassphraseEnterTil.error = null
@ -91,9 +89,9 @@ class KeysBackupSetupStep2Fragment @Inject constructor() : VectorBaseFragment<Fr
views.keysBackupSetupStep2PassphraseEnterTil.error = null
}
}
})
}
viewModel.passphrase.observe(viewLifecycleOwner, Observer<String> { newValue ->
viewModel.passphrase.observe(viewLifecycleOwner) { newValue ->
if (newValue.isEmpty()) {
viewModel.passwordStrength.value = null
} else {
@ -104,28 +102,28 @@ class KeysBackupSetupStep2Fragment @Inject constructor() : VectorBaseFragment<Fr
}
}
}
})
}
views.keysBackupSetupStep2PassphraseEnterEdittext.setText(viewModel.passphrase.value)
viewModel.passphraseError.observe(viewLifecycleOwner, Observer {
viewModel.passphraseError.observe(viewLifecycleOwner) {
TransitionManager.beginDelayedTransition(views.keysBackupRoot)
views.keysBackupSetupStep2PassphraseEnterTil.error = it
})
}
views.keysBackupSetupStep2PassphraseConfirmEditText.setText(viewModel.confirmPassphrase.value)
viewModel.showPasswordMode.observe(viewLifecycleOwner, Observer {
viewModel.showPasswordMode.observe(viewLifecycleOwner) {
val shouldBeVisible = it ?: false
views.keysBackupSetupStep2PassphraseEnterEdittext.showPassword(shouldBeVisible)
views.keysBackupSetupStep2PassphraseConfirmEditText.showPassword(shouldBeVisible)
views.keysBackupSetupStep2ShowPassword.setImageResource(if (shouldBeVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
})
views.keysBackupSetupStep2ShowPassword.render(shouldBeVisible)
}
viewModel.confirmPassphraseError.observe(viewLifecycleOwner, Observer {
viewModel.confirmPassphraseError.observe(viewLifecycleOwner) {
TransitionManager.beginDelayedTransition(views.keysBackupRoot)
views.keysBackupSetupStep2PassphraseConfirmTil.error = it
})
}
views.keysBackupSetupStep2PassphraseConfirmEditText.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) {

View File

@ -25,7 +25,6 @@ import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import arrow.core.Try
import com.google.android.material.bottomsheet.BottomSheetDialog
import im.vector.app.R
@ -61,7 +60,7 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment<Fr
viewModel.shouldPromptOnBack = false
viewModel.passphrase.observe(viewLifecycleOwner, Observer {
viewModel.passphrase.observe(viewLifecycleOwner) {
if (it.isNullOrBlank()) {
// Recovery was generated, so show key and options to save
views.keysBackupSetupStep3Label2.text = getString(R.string.keys_backup_setup_step3_text_line2_no_passphrase)
@ -81,7 +80,7 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment<Fr
views.keysBackupSetupStep3FinishButton.text = getString(R.string.keys_backup_setup_step3_button_title)
views.keysBackupSetupStep3RecoveryKeyText.isVisible = false
}
})
}
setupViews()
}

View File

@ -183,11 +183,11 @@ class KeyRequestHandler @Inject constructor(
denyAllRequests(mappingKey)
}
alert.addButton(context.getString(R.string.share_without_verifying_short_label), Runnable {
alert.addButton(context.getString(R.string.share_without_verifying_short_label), {
shareAllSessions(mappingKey)
})
alert.addButton(context.getString(R.string.ignore_request_short_label), Runnable {
alert.addButton(context.getString(R.string.ignore_request_short_label), {
denyAllRequests(mappingKey)
})

View File

@ -106,6 +106,6 @@ class SharedSecuredStoragePassphraseFragment @Inject constructor(
override fun invalidate() = withState(sharedViewModel) { state ->
val shouldBeVisible = state.passphraseVisible
views.ssssPassphraseEnterEdittext.showPassword(shouldBeVisible)
views.ssssViewShowPassword.setImageResource(if (shouldBeVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
views.ssssViewShowPassword.render(shouldBeVisible)
}
}

View File

@ -97,7 +97,7 @@ class BackupToQuadSMigrationTask @Inject constructor(
when {
params.passphrase?.isNotEmpty() == true -> {
reportProgress(params, R.string.bootstrap_progress_generating_ssss)
awaitCallback<SsssKeyCreationInfo> {
awaitCallback {
quadS.generateKeyWithPassphrase(
UUID.randomUUID().toString(),
"ssss_key",

View File

@ -109,7 +109,7 @@ class BootstrapConfirmPassphraseFragment @Inject constructor()
if (state.step is BootstrapStep.ConfirmPassphrase) {
val isPasswordVisible = state.step.isPasswordVisible
views.ssssPassphraseEnterEdittext.showPassword(isPasswordVisible, updateCursor = false)
views.ssssViewShowPassword.setImageResource(if (isPasswordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
views.ssssViewShowPassword.render(isPasswordVisible)
}
}
}

View File

@ -103,7 +103,7 @@ class BootstrapEnterPassphraseFragment @Inject constructor()
if (state.step is BootstrapStep.SetupPassphrase) {
val isPasswordVisible = state.step.isPasswordVisible
views.ssssPassphraseEnterEdittext.showPassword(isPasswordVisible, updateCursor = false)
views.ssssViewShowPassword.setImageResource(if (isPasswordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
views.ssssViewShowPassword.render(isPasswordVisible)
state.passphraseStrength.invoke()?.let { strength ->
val score = strength.score

View File

@ -133,7 +133,7 @@ class BootstrapMigrateBackupFragment @Inject constructor(
if (state.step is BootstrapStep.GetBackupSecretPassForMigration) {
val isPasswordVisible = state.step.isPasswordVisible
views.bootstrapMigrateEditText.showPassword(isPasswordVisible, updateCursor = false)
views.bootstrapMigrateShowPassword.setImageResource(if (isPasswordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
views.bootstrapMigrateShowPassword.render(isPasswordVisible)
}
views.bootstrapDescriptionText.text = getString(R.string.bootstrap_migration_enter_backup_password)

View File

@ -62,8 +62,7 @@ class BootstrapSaveRecoveryKeyFragment @Inject constructor(
}
}
private fun downloadRecoveryKey() = withState(sharedViewModel) { _ ->
private fun downloadRecoveryKey() {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "text/plain"

View File

@ -1,32 +0,0 @@
/*
* 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.app.features.crypto.util
import androidx.annotation.DrawableRes
import im.vector.app.R
import im.vector.app.core.extensions.exhaustive
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
@DrawableRes
fun RoomEncryptionTrustLevel?.toImageRes(): Int {
return when (this) {
null -> 0
RoomEncryptionTrustLevel.Default -> R.drawable.ic_shield_black
RoomEncryptionTrustLevel.Warning -> R.drawable.ic_shield_warning
RoomEncryptionTrustLevel.Trusted -> R.drawable.ic_shield_trusted
}.exhaustive
}

View File

@ -92,13 +92,11 @@ class IncomingVerificationRequestHandler @Inject constructor(
}
addButton(
context.getString(R.string.ignore),
Runnable {
tx.cancel()
}
{ tx.cancel() }
)
addButton(
context.getString(R.string.action_open),
Runnable {
{
(weakCurrentActivity?.get() as? VectorBaseActivity<*>)?.let {
it.navigator.performDeviceVerification(it, tx.otherUserId, tx.transactionId)
}

View File

@ -24,7 +24,6 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.fragmentViewModel
@ -49,6 +48,7 @@ import im.vector.app.features.crypto.verification.request.VerificationRequestFra
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.settings.VectorSettingsActivity
import kotlinx.parcelize.Parcelize
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
@ -162,23 +162,22 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetV
if (state.sasTransactionState == VerificationTxState.Verified
|| state.qrTransactionState == VerificationTxState.Verified
|| state.verifiedFromPrivateKeys) {
views.otherUserShield.setImageResource(R.drawable.ic_shield_trusted)
views.otherUserShield.render(RoomEncryptionTrustLevel.Trusted)
} else {
views.otherUserShield.setImageResource(R.drawable.ic_shield_warning)
views.otherUserShield.render(RoomEncryptionTrustLevel.Warning)
}
views.otherUserNameText.text = getString(
if (state.selfVerificationMode) R.string.crosssigning_verify_this_session else R.string.crosssigning_verify_session
)
views.otherUserShield.isVisible = true
} else {
avatarRenderer.render(matrixItem, views.otherUserAvatarImageView)
if (state.sasTransactionState == VerificationTxState.Verified || state.qrTransactionState == VerificationTxState.Verified) {
views.otherUserNameText.text = getString(R.string.verification_verified_user, matrixItem.getBestName())
views.otherUserShield.isVisible = true
views.otherUserShield.render(RoomEncryptionTrustLevel.Trusted)
} else {
views.otherUserNameText.text = getString(R.string.verification_verify_user, matrixItem.getBestName())
views.otherUserShield.isVisible = false
views.otherUserShield.render(null)
}
}
}

View File

@ -25,6 +25,7 @@ import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationA
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
import im.vector.app.features.html.EventHtmlRenderer
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import javax.inject.Inject
class VerificationConclusionController @Inject constructor(
@ -56,7 +57,7 @@ class VerificationConclusionController @Inject constructor(
bottomSheetVerificationBigImageItem {
id("image")
imageRes(R.drawable.ic_shield_trusted)
roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Trusted)
}
bottomDone()
@ -69,7 +70,7 @@ class VerificationConclusionController @Inject constructor(
bottomSheetVerificationBigImageItem {
id("image")
imageRes(R.drawable.ic_shield_warning)
roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Warning)
}
bottomSheetVerificationNoticeItem {

View File

@ -16,13 +16,13 @@
*/
package im.vector.app.features.crypto.verification.epoxy
import android.widget.ImageView
import androidx.core.view.ViewCompat
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.ui.views.ShieldImageView
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
/**
* A action for bottom sheet.
@ -31,24 +31,14 @@ import im.vector.app.core.epoxy.VectorEpoxyModel
abstract class BottomSheetVerificationBigImageItem : VectorEpoxyModel<BottomSheetVerificationBigImageItem.Holder>() {
@EpoxyAttribute
var imageRes: Int = 0
@EpoxyAttribute
var contentDescription: String? = null
lateinit var roomEncryptionTrustLevel: RoomEncryptionTrustLevel
override fun bind(holder: Holder) {
super.bind(holder)
holder.image.setImageResource(imageRes)
if (contentDescription == null) {
ViewCompat.setImportantForAccessibility(holder.image, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO)
} else {
ViewCompat.setImportantForAccessibility(holder.image, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES)
holder.image.contentDescription = contentDescription
}
holder.image.render(roomEncryptionTrustLevel)
}
class Holder : VectorEpoxyHolder() {
val image by bind<ImageView>(R.id.itemVerificationBigImage)
val image by bind<ShieldImageView>(R.id.itemVerificationBigImage)
}
}

View File

@ -23,6 +23,7 @@ import im.vector.app.core.resources.StringProvider
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationWaitingItem
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import javax.inject.Inject
class VerificationQRWaitingController @Inject constructor(
@ -49,7 +50,7 @@ class VerificationQRWaitingController @Inject constructor(
bottomSheetVerificationBigImageItem {
id("image")
imageRes(R.drawable.ic_shield_trusted)
roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Trusted)
}
bottomSheetVerificationWaitingItem {

View File

@ -25,6 +25,7 @@ import im.vector.app.features.crypto.verification.VerificationBottomSheetViewSta
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import javax.inject.Inject
class VerificationQrScannedByOtherController @Inject constructor(
@ -58,7 +59,7 @@ class VerificationQrScannedByOtherController @Inject constructor(
bottomSheetVerificationBigImageItem {
id("image")
imageRes(R.drawable.ic_shield_trusted)
roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Trusted)
}
dividerItem {

View File

@ -29,7 +29,6 @@ import im.vector.app.R
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import io.reactivex.Observable
import io.reactivex.functions.BiFunction
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session
@ -123,7 +122,7 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro
session
.rx()
.liveGroupSummaries(groupSummariesQueryParams),
BiFunction { allCommunityGroup, communityGroups ->
{ allCommunityGroup, communityGroups ->
listOf(allCommunityGroup) + communityGroups
}
)

View File

@ -285,10 +285,10 @@ class HomeActivity :
dismissedAction = Runnable {
homeActivityViewModel.handle(HomeActivityViewActions.PushPromptHasBeenReviewed)
}
addButton(getString(R.string.dismiss), Runnable {
addButton(getString(R.string.dismiss), {
homeActivityViewModel.handle(HomeActivityViewActions.PushPromptHasBeenReviewed)
}, true)
addButton(getString(R.string.settings), Runnable {
addButton(getString(R.string.settings), {
(weakCurrentActivity?.get() as? VectorBaseActivity<*>)?.let {
// action(it)
homeActivityViewModel.handle(HomeActivityViewActions.PushPromptHasBeenReviewed)

View File

@ -32,7 +32,6 @@ import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.features.settings.VectorPreferences
import io.reactivex.Observable
import io.reactivex.functions.Function3
import org.matrix.android.sdk.api.NoOpMatrixCallback
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.Session
@ -103,7 +102,7 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted
session.rx().liveUserCryptoDevices(session.myUserId),
session.rx().liveMyDevicesInfo(),
session.rx().liveCrossSigningPrivateKeys(),
Function3 { cryptoList, infoList, pInfo ->
{ cryptoList, infoList, pInfo ->
// Timber.v("## Detector trigger ${cryptoList.map { "${it.deviceId} ${it.trustLevel}" }}")
// Timber.v("## Detector trigger canCrossSign ${pInfo.get().selfSigned != null}")
infoList

View File

@ -16,7 +16,6 @@
package im.vector.app.features.home.room.breadcrumbs
import android.view.View
import com.airbnb.epoxy.EpoxyController
import im.vector.app.core.epoxy.zeroItem
import im.vector.app.core.utils.DebouncedClickListener
@ -65,7 +64,7 @@ class BreadcrumbsController @Inject constructor(
hasUnreadMessage(it.hasUnreadMessages)
hasDraft(it.userDrafts.isNotEmpty())
itemClickListener(
DebouncedClickListener(View.OnClickListener { _ ->
DebouncedClickListener({ _ ->
listener?.onBreadcrumbClicked(it.roomId)
})
)

View File

@ -46,6 +46,7 @@ abstract class BreadcrumbsItem : VectorEpoxyModel<BreadcrumbsItem.Holder>() {
holder.rootView.setOnClickListener(itemClickListener)
holder.unreadIndentIndicator.isVisible = hasUnreadMessage
avatarRenderer.render(matrixItem, holder.avatarImageView)
holder.avatarImageView.contentDescription = matrixItem.getBestName()
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted))
holder.draftIndentIndicator.isVisible = hasDraft
holder.typingIndicator.isVisible = hasTypingUsers

View File

@ -60,9 +60,9 @@ class JumpToBottomViewVisibilityManager(
}
fun maybeShowJumpToBottomViewVisibilityWithDelay() {
debouncer.debounce("jump_to_bottom_visibility", 250, Runnable {
debouncer.debounce("jump_to_bottom_visibility", 250) {
maybeShowJumpToBottomViewVisibility()
})
}
}
private fun maybeShowJumpToBottomViewVisibility() {

View File

@ -124,7 +124,6 @@ import im.vector.app.features.call.conference.JitsiCallViewModel
import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.command.Command
import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivity
import im.vector.app.features.crypto.util.toImageRes
import im.vector.app.features.crypto.verification.VerificationBottomSheet
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.composer.TextComposerView
@ -537,8 +536,18 @@ class RoomDetailFragment @Inject constructor(
.Builder
.fromRootView(views.rootConstraintLayout)
.setKeyboardAnimationStyle(R.style.emoji_fade_animation_style)
.setOnEmojiPopupShownListener { views.composerLayout.views.composerEmojiButton.setImageResource(R.drawable.ic_keyboard) }
.setOnEmojiPopupDismissListener { views.composerLayout.views.composerEmojiButton.setImageResource(R.drawable.ic_insert_emoji) }
.setOnEmojiPopupShownListener {
views.composerLayout.views.composerEmojiButton.let {
it.setImageResource(R.drawable.ic_keyboard)
it.contentDescription = getString(R.string.a11y_close_emoji_picker)
}
}
.setOnEmojiPopupDismissListener {
views.composerLayout.views.composerEmojiButton.let {
it.setImageResource(R.drawable.ic_insert_emoji)
it.contentDescription = getString(R.string.a11y_open_emoji_picker)
}
}
.build(views.composerLayout.views.composerEditText)
views.composerLayout.views.composerEmojiButton.debouncedClicks {
@ -1193,10 +1202,7 @@ class RoomDetailFragment @Inject constructor(
avatarRenderer.render(roomSummary.toMatrixItem(), views.roomToolbarAvatarImageView)
renderSubTitle(typingMessage, roomSummary.topic)
views.roomToolbarDecorationImageView.let {
it.setImageResource(roomSummary.roomEncryptionTrustLevel.toImageRes())
it.isVisible = roomSummary.roomEncryptionTrustLevel != null
}
views.roomToolbarDecorationImageView.render(roomSummary.roomEncryptionTrustLevel)
}
}

View File

@ -49,7 +49,6 @@ import im.vector.app.features.raw.wellknown.getElementWellknown
import im.vector.app.features.settings.VectorLocale
import im.vector.app.features.settings.VectorPreferences
import io.reactivex.Observable
import io.reactivex.functions.BiFunction
import io.reactivex.rxkotlin.subscribeBy
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.Dispatchers
@ -1331,7 +1330,7 @@ class RoomDetailViewModel @AssistedInject constructor(
.combineLatest<List<TimelineEvent>, RoomSummary, UnreadState>(
timelineEvents.observeOn(Schedulers.computation()),
room.rx().liveRoomSummary().unwrap(),
BiFunction { timelineEvents, roomSummary ->
{ timelineEvents, roomSummary ->
computeUnreadState(timelineEvents, roomSummary)
}
)

View File

@ -24,7 +24,6 @@ import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.text.toSpannable
import androidx.core.view.isVisible
import androidx.transition.ChangeBounds
import androidx.transition.Fade
import androidx.transition.Transition
@ -143,16 +142,10 @@ class TextComposerView @JvmOverloads constructor(
fun setRoomEncrypted(isEncrypted: Boolean, roomEncryptionTrustLevel: RoomEncryptionTrustLevel?) {
if (isEncrypted) {
views.composerEditText.setHint(R.string.room_message_placeholder)
views.composerShieldImageView.isVisible = true
val shieldRes = when (roomEncryptionTrustLevel) {
RoomEncryptionTrustLevel.Trusted -> R.drawable.ic_shield_trusted
RoomEncryptionTrustLevel.Warning -> R.drawable.ic_shield_warning
else -> R.drawable.ic_shield_black
}
views.composerShieldImageView.setImageResource(shieldRes)
views.composerShieldImageView.render(roomEncryptionTrustLevel)
} else {
views.composerEditText.setHint(R.string.room_message_placeholder)
views.composerShieldImageView.isVisible = false
views.composerShieldImageView.render(null)
}
}
}

View File

@ -16,7 +16,6 @@
package im.vector.app.features.home.room.detail.timeline.factory
import android.view.View
import im.vector.app.R
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.home.AvatarRenderer
@ -42,7 +41,7 @@ class DefaultItemFactory @Inject constructor(private val avatarSizeProvider: Ava
avatarRenderer = avatarRenderer,
informationData = informationData,
text = text,
itemLongClickListener = View.OnLongClickListener { view ->
itemLongClickListener = { view ->
callback?.onEventLongClicked(informationData, null, view) ?: false
},
readReceiptsCallback = callback

View File

@ -322,7 +322,7 @@ class MessageItemFactory @Inject constructor(
mode(ImageContentRenderer.Mode.STICKER)
} else {
clickListener(
DebouncedClickListener(View.OnClickListener { view ->
DebouncedClickListener({ view ->
callback?.onImageMessageClicked(messageContent, data, view)
}))
}

View File

@ -16,7 +16,6 @@
package im.vector.app.features.home.room.detail.timeline.factory
import android.view.View
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
import im.vector.app.features.home.room.detail.timeline.format.NoticeEventFormatter
@ -41,7 +40,7 @@ class NoticeItemFactory @Inject constructor(private val eventFormatter: NoticeEv
avatarRenderer = avatarRenderer,
informationData = informationData,
noticeText = formattedText,
itemLongClickListener = View.OnLongClickListener { view ->
itemLongClickListener = { view ->
callback?.onEventLongClicked(informationData, null, view) ?: false
},
readReceiptsCallback = callback,

View File

@ -15,7 +15,6 @@
*/
package im.vector.app.features.home.room.detail.timeline.helper
import android.view.View
import im.vector.app.EmojiCompatFontProvider
import im.vector.app.core.utils.DebouncedClickListener
import im.vector.app.features.home.AvatarRenderer
@ -39,13 +38,13 @@ class MessageItemAttributesFactory @Inject constructor(
informationData = informationData,
avatarRenderer = avatarRenderer,
messageColorProvider = messageColorProvider,
itemLongClickListener = View.OnLongClickListener { view ->
itemLongClickListener = { view ->
callback?.onEventLongClicked(informationData, messageContent, view) ?: false
},
itemClickListener = DebouncedClickListener(View.OnClickListener { view ->
itemClickListener = DebouncedClickListener({ view ->
callback?.onEventCellClicked(informationData, messageContent, view)
}),
memberClickListener = DebouncedClickListener(View.OnClickListener {
memberClickListener = DebouncedClickListener({
callback?.onMemberNameClicked(informationData)
}),
reactionPillCallback = callback,

View File

@ -23,11 +23,13 @@ import android.widget.TextView
import androidx.annotation.IdRes
import androidx.core.view.isVisible
import im.vector.app.R
import im.vector.app.core.ui.views.ShieldImageView
import im.vector.app.core.utils.DebouncedClickListener
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.timeline.MessageColorProvider
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
import im.vector.app.features.reactions.widget.ReactionButton
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.session.room.send.SendState
/**
@ -39,7 +41,7 @@ abstract class AbsBaseMessageItem<H : AbsBaseMessageItem.Holder> : BaseEventItem
abstract val baseAttributes: Attributes
private val _readReceiptsClickListener = DebouncedClickListener(View.OnClickListener {
private val _readReceiptsClickListener = DebouncedClickListener({
baseAttributes.readReceiptsCallback?.onReadReceiptsClicked(baseAttributes.informationData.readReceipts)
})
@ -94,13 +96,12 @@ abstract class AbsBaseMessageItem<H : AbsBaseMessageItem.Holder> : BaseEventItem
when (baseAttributes.informationData.e2eDecoration) {
E2EDecoration.NONE -> {
holder.e2EDecorationView.isVisible = false
holder.e2EDecorationView.render(null)
}
E2EDecoration.WARN_IN_CLEAR,
E2EDecoration.WARN_SENT_BY_UNVERIFIED,
E2EDecoration.WARN_SENT_BY_UNKNOWN -> {
holder.e2EDecorationView.setImageResource(R.drawable.ic_shield_warning)
holder.e2EDecorationView.isVisible = true
holder.e2EDecorationView.render(RoomEncryptionTrustLevel.Warning)
}
}
@ -123,7 +124,7 @@ abstract class AbsBaseMessageItem<H : AbsBaseMessageItem.Holder> : BaseEventItem
abstract class Holder(@IdRes stubId: Int) : BaseEventItem.BaseHolder(stubId) {
val reactionsContainer by bind<ViewGroup>(R.id.reactionsContainer)
val e2EDecorationView by bind<ImageView>(R.id.messageE2EDecoration)
val e2EDecorationView by bind<ShieldImageView>(R.id.messageE2EDecoration)
}
/**

View File

@ -42,10 +42,10 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
@EpoxyAttribute
lateinit var attributes: Attributes
private val _avatarClickListener = DebouncedClickListener(View.OnClickListener {
private val _avatarClickListener = DebouncedClickListener({
attributes.avatarCallback?.onAvatarClicked(attributes.informationData)
})
private val _memberNameClickListener = DebouncedClickListener(View.OnClickListener {
private val _memberNameClickListener = DebouncedClickListener({
attributes.avatarCallback?.onMemberNameClicked(attributes.informationData)
})

View File

@ -32,7 +32,7 @@ abstract class DefaultItem : BaseEventItem<DefaultItem.Holder>() {
@EpoxyAttribute
lateinit var attributes: Attributes
private val _readReceiptsClickListener = DebouncedClickListener(View.OnClickListener {
private val _readReceiptsClickListener = DebouncedClickListener({
attributes.readReceiptsCallback?.onReadReceiptsClicked(attributes.informationData.readReceipts)
})

View File

@ -186,14 +186,14 @@ abstract class MergedRoomCreationItem : BasedMergedItem<MergedRoomCreationItem.H
holder.setAvatarButton.isVisible = shouldSetAvatar
if (shouldSetAvatar) {
holder.setAvatarButton.setOnClickListener(DebouncedClickListener({ _ ->
holder.setAvatarButton.setOnClickListener(DebouncedClickListener({
attributes.callback?.onTimelineItemAction(RoomDetailAction.QuickActionSetAvatar)
}))
}
holder.addPeopleButton.isVisible = !isDirect
if (!isDirect) {
holder.addPeopleButton.setOnClickListener(DebouncedClickListener({ _ ->
holder.addPeopleButton.setOnClickListener(DebouncedClickListener({
attributes.callback?.onTimelineItemAction(RoomDetailAction.QuickActionInvitePeople)
}))
}

View File

@ -143,7 +143,7 @@ abstract class MessagePollItem : AbsMessageItem<MessagePollItem.Holder>() {
override fun bindView(itemView: View) {
super.bindView(itemView)
val buttons = listOf(button1, button2, button3, button4, button5)
val clickListener = DebouncedClickListener(View.OnClickListener {
val clickListener = DebouncedClickListener({
val optionIndex = buttons.indexOf(it)
if (optionIndex != -1 && pollId != null) {
val compatValue = if (optionIndex < optionValues?.size ?: 0) optionValues?.get(optionIndex) else null

View File

@ -19,15 +19,16 @@ package im.vector.app.features.home.room.detail.timeline.item
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.ui.views.ShieldImageView
import im.vector.app.core.utils.DebouncedClickListener
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
@EpoxyModelClass(layout = R.layout.item_timeline_event_base_noinfo)
abstract class NoticeItem : BaseEventItem<NoticeItem.Holder>() {
@ -35,7 +36,7 @@ abstract class NoticeItem : BaseEventItem<NoticeItem.Holder>() {
@EpoxyAttribute
lateinit var attributes: Attributes
private val _readReceiptsClickListener = DebouncedClickListener(View.OnClickListener {
private val _readReceiptsClickListener = DebouncedClickListener({
attributes.readReceiptsCallback?.onReadReceiptsClicked(attributes.informationData.readReceipts)
})
@ -49,13 +50,12 @@ abstract class NoticeItem : BaseEventItem<NoticeItem.Holder>() {
when (attributes.informationData.e2eDecoration) {
E2EDecoration.NONE -> {
holder.e2EDecorationView.isVisible = false
holder.e2EDecorationView.render(null)
}
E2EDecoration.WARN_IN_CLEAR,
E2EDecoration.WARN_SENT_BY_UNVERIFIED,
E2EDecoration.WARN_SENT_BY_UNKNOWN -> {
holder.e2EDecorationView.setImageResource(R.drawable.ic_shield_warning)
holder.e2EDecorationView.isVisible = true
holder.e2EDecorationView.render(RoomEncryptionTrustLevel.Warning)
}
}
}
@ -75,7 +75,7 @@ abstract class NoticeItem : BaseEventItem<NoticeItem.Holder>() {
class Holder : BaseHolder(STUB_ID) {
val avatarImageView by bind<ImageView>(R.id.itemNoticeAvatarView)
val noticeTextView by bind<TextView>(R.id.itemNoticeTextView)
val e2EDecorationView by bind<ImageView>(R.id.messageE2EDecoration)
val e2EDecorationView by bind<ShieldImageView>(R.id.messageE2EDecoration)
}
data class Attributes(

View File

@ -60,7 +60,7 @@ data class ReactionInfo(
*/
class ViewReactionsViewModel @AssistedInject constructor(@Assisted
initialState: DisplayReactionsViewState,
private val session: Session,
session: Session,
private val dateFormatter: VectorDateFormatter
) : VectorViewModel<DisplayReactionsViewState, EmptyAction, EmptyViewEvents>(initialState) {

View File

@ -31,7 +31,7 @@ import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.features.crypto.util.toImageRes
import im.vector.app.core.ui.views.ShieldImageView
import im.vector.app.features.home.AvatarRenderer
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.util.MatrixItem
@ -73,8 +73,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
holder.unreadIndentIndicator.isVisible = hasUnreadMessage
holder.draftView.isVisible = hasDraft
avatarRenderer.render(matrixItem, holder.avatarImageView)
holder.roomAvatarDecorationImageView.isVisible = encryptionTrustLevel != null
holder.roomAvatarDecorationImageView.setImageResource(encryptionTrustLevel.toImageRes())
holder.roomAvatarDecorationImageView.render(encryptionTrustLevel)
holder.roomAvatarFailSendingImageView.isVisible = hasFailedSending
renderSelection(holder, showSelected)
holder.typingView.setTextOrHide(typingMessage)
@ -110,7 +109,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
val lastEventTimeView by bind<TextView>(R.id.roomLastEventTimeView)
val avatarCheckedImageView by bind<ImageView>(R.id.roomAvatarCheckedImageView)
val avatarImageView by bind<ImageView>(R.id.roomAvatarImageView)
val roomAvatarDecorationImageView by bind<ImageView>(R.id.roomAvatarDecorationImageView)
val roomAvatarDecorationImageView by bind<ShieldImageView>(R.id.roomAvatarDecorationImageView)
val roomAvatarFailSendingImageView by bind<ImageView>(R.id.roomAvatarFailSendingImageView)
val rootView by bind<ViewGroup>(R.id.itemRoomLayout)
}

View File

@ -16,7 +16,6 @@
package im.vector.app.features.home.room.list
import android.view.View
import im.vector.app.R
import im.vector.app.core.date.DateFormatKind
import im.vector.app.core.date.VectorDateFormatter
@ -109,7 +108,7 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
onLongClick?.invoke(roomSummary) ?: false
}
.itemClickListener(
DebouncedClickListener(View.OnClickListener { _ ->
DebouncedClickListener({
onClick?.invoke(roomSummary)
})
)

View File

@ -29,7 +29,7 @@ import org.matrix.android.sdk.rx.rx
import org.matrix.android.sdk.rx.unwrap
class RoomListQuickActionsViewModel @AssistedInject constructor(@Assisted initialState: RoomListQuickActionsState,
private val session: Session
session: Session
) : VectorViewModel<RoomListQuickActionsState, EmptyAction, EmptyViewEvents>(initialState) {
@AssistedFactory

View File

@ -36,7 +36,6 @@ import im.vector.app.core.extensions.showPassword
import im.vector.app.core.extensions.toReducedUrl
import im.vector.app.databinding.FragmentLoginBinding
import io.reactivex.Observable
import io.reactivex.functions.BiFunction
import io.reactivex.rxkotlin.subscribeBy
import org.matrix.android.sdk.api.failure.Failure
@ -225,7 +224,7 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment<FragmentLog
.combineLatest(
views.loginField.textChanges().map { it.trim().isNotEmpty() },
views.passwordField.textChanges().map { it.isNotEmpty() },
BiFunction<Boolean, Boolean, Boolean> { isLoginNotEmpty, isPasswordNotEmpty ->
{ isLoginNotEmpty, isPasswordNotEmpty ->
isLoginNotEmpty && isPasswordNotEmpty
}
)
@ -255,14 +254,7 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment<FragmentLog
private fun renderPasswordField() {
views.passwordField.showPassword(passwordShown)
if (passwordShown) {
views.passwordReveal.setImageResource(R.drawable.ic_eye_closed)
views.passwordReveal.contentDescription = getString(R.string.a11y_hide_password)
} else {
views.passwordReveal.setImageResource(R.drawable.ic_eye)
views.passwordReveal.contentDescription = getString(R.string.a11y_show_password)
}
views.passwordReveal.render(passwordShown)
}
override fun resetViewModel() {

View File

@ -32,7 +32,6 @@ import im.vector.app.core.extensions.showPassword
import im.vector.app.core.extensions.toReducedUrl
import im.vector.app.databinding.FragmentLoginResetPasswordBinding
import io.reactivex.Observable
import io.reactivex.functions.BiFunction
import io.reactivex.rxkotlin.subscribeBy
import javax.inject.Inject
@ -69,7 +68,7 @@ class LoginResetPasswordFragment @Inject constructor() : AbstractLoginFragment<F
.combineLatest(
views.resetPasswordEmail.textChanges().map { it.isEmail() },
views.passwordField.textChanges().map { it.isNotEmpty() },
BiFunction<Boolean, Boolean, Boolean> { isEmail, isPasswordNotEmpty ->
{ isEmail, isPasswordNotEmpty ->
isEmail && isPasswordNotEmpty
}
)
@ -127,14 +126,7 @@ class LoginResetPasswordFragment @Inject constructor() : AbstractLoginFragment<F
private fun renderPasswordField() {
views.passwordField.showPassword(passwordShown)
if (passwordShown) {
views.passwordReveal.setImageResource(R.drawable.ic_eye_closed)
views.passwordReveal.contentDescription = getString(R.string.a11y_hide_password)
} else {
views.passwordReveal.setImageResource(R.drawable.ic_eye)
views.passwordReveal.contentDescription = getString(R.string.a11y_show_password)
}
views.passwordReveal.render(passwordShown)
}
override fun resetViewModel() {
@ -153,9 +145,7 @@ class LoginResetPasswordFragment @Inject constructor() : AbstractLoginFragment<F
is Fail -> {
views.resetPasswordEmailTil.error = errorFormatter.toHumanReadable(state.asyncResetPassword.error)
}
is Success -> {
Unit
}
is Success -> Unit
}
}
}

View File

@ -74,9 +74,7 @@ class LoginResetPasswordMailConfirmationFragment @Inject constructor() : Abstrac
.setPositiveButton(R.string.ok, null)
.show()
}
is Success -> {
Unit
}
is Success -> Unit
}
}
}

View File

@ -138,7 +138,7 @@ class MatrixToBottomSheet :
fun withLink(matrixToLink: String, listener: InteractionListener?): MatrixToBottomSheet {
return MatrixToBottomSheet().apply {
arguments = Bundle().apply {
putParcelable(MvRx.KEY_ARG, MatrixToBottomSheet.MatrixToArgs(
putParcelable(MvRx.KEY_ARG, MatrixToArgs(
matrixToLink = matrixToLink
))
}

View File

@ -379,7 +379,7 @@ class BugReporter @Inject constructor(
if (responseCode != HttpURLConnection.HTTP_OK) {
if (null != errorMessage) {
serverError = "Failed with error $errorMessage"
} else if (null == response || null == response.body) {
} else if (response?.body == null) {
serverError = "Failed with error $responseCode"
} else {
try {

View File

@ -26,7 +26,6 @@ import android.view.MenuItem
import android.widget.SearchView
import androidx.appcompat.widget.Toolbar
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import com.airbnb.mvrx.viewModel
import com.google.android.material.tabs.TabLayout
import com.jakewharton.rxbinding3.widget.queryTextChanges
@ -107,13 +106,13 @@ class EmojiReactionPickerActivity : VectorBaseActivity<ActivityEmojiReactionPick
}
views.tabs.addOnTabSelectedListener(tabLayoutSelectionListener)
viewModel.currentSection.observe(this, Observer { section ->
viewModel.currentSection.observe(this) { section ->
section?.let {
views.tabs.removeOnTabSelectedListener(tabLayoutSelectionListener)
views.tabs.getTabAt(it)?.select()
views.tabs.addOnTabSelectedListener(tabLayoutSelectionListener)
}
})
}
viewModel.navigateEvent.observeEvent(this) {
if (it == EmojiChooserViewModel.NAVIGATE_FINISH) {

View File

@ -53,6 +53,7 @@ import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore
import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet
import im.vector.app.features.roommemberprofile.powerlevel.EditPowerLevelDialogs
import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.session.room.powerlevels.Role
import org.matrix.android.sdk.api.util.MatrixItem
import javax.inject.Inject
@ -205,31 +206,28 @@ class RoomMemberProfileFragment @Inject constructor(
if (state.isRoomEncrypted) {
headerViews.memberProfileDecorationImageView.isVisible = true
if (state.userMXCrossSigningInfo != null) {
val trustLevel = if (state.userMXCrossSigningInfo != null) {
// Cross signing is enabled for this user
val icon = if (state.userMXCrossSigningInfo.isTrusted()) {
if (state.userMXCrossSigningInfo.isTrusted()) {
// User is trusted
if (state.allDevicesAreCrossSignedTrusted) {
R.drawable.ic_shield_trusted
RoomEncryptionTrustLevel.Trusted
} else {
R.drawable.ic_shield_warning
RoomEncryptionTrustLevel.Warning
}
} else {
R.drawable.ic_shield_black
RoomEncryptionTrustLevel.Default
}
headerViews.memberProfileDecorationImageView.setImageResource(icon)
views.matrixProfileDecorationToolbarAvatarImageView.setImageResource(icon)
} else {
// Legacy
if (state.allDevicesAreTrusted) {
headerViews.memberProfileDecorationImageView.setImageResource(R.drawable.ic_shield_trusted)
views.matrixProfileDecorationToolbarAvatarImageView.setImageResource(R.drawable.ic_shield_trusted)
RoomEncryptionTrustLevel.Trusted
} else {
headerViews.memberProfileDecorationImageView.setImageResource(R.drawable.ic_shield_warning)
views.matrixProfileDecorationToolbarAvatarImageView.setImageResource(R.drawable.ic_shield_warning)
RoomEncryptionTrustLevel.Warning
}
}
headerViews.memberProfileDecorationImageView.render(trustLevel)
views.matrixProfileDecorationToolbarAvatarImageView.render(trustLevel)
} else {
headerViews.memberProfileDecorationImageView.isVisible = false
}

View File

@ -44,7 +44,6 @@ import im.vector.app.core.utils.copyToClipboard
import im.vector.app.core.utils.startSharePlainTextIntent
import im.vector.app.databinding.FragmentMatrixProfileBinding
import im.vector.app.databinding.ViewStubRoomProfileHeaderBinding
import im.vector.app.features.crypto.util.toImageRes
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.list.actions.RoomListActionsArgs
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
@ -204,9 +203,8 @@ class RoomProfileFragment @Inject constructor(
val matrixItem = it.toMatrixItem()
avatarRenderer.render(matrixItem, headerViews.roomProfileAvatarView)
avatarRenderer.render(matrixItem, views.matrixProfileToolbarAvatarImageView)
headerViews.roomProfileDecorationImageView.isVisible = it.roomEncryptionTrustLevel != null
headerViews.roomProfileDecorationImageView.setImageResource(it.roomEncryptionTrustLevel.toImageRes())
views.matrixProfileDecorationToolbarAvatarImageView.setImageResource(it.roomEncryptionTrustLevel.toImageRes())
headerViews.roomProfileDecorationImageView.render(it.roomEncryptionTrustLevel)
views.matrixProfileDecorationToolbarAvatarImageView.render(it.roomEncryptionTrustLevel)
}
}
roomProfileController.setData(state)

View File

@ -29,7 +29,6 @@ import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.BiFunction
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.extensions.orFalse
@ -90,7 +89,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState
.liveStateEvent(EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition)
.mapOptional { it.content.toModel<PowerLevelsContent>() }
.unwrap(),
BiFunction { roomMembers, powerLevelsContent ->
{ roomMembers, powerLevelsContent ->
buildRoomMemberSummaries(powerLevelsContent, roomMembers)
}
)

View File

@ -199,7 +199,6 @@ class RoomSettingsFragment @Inject constructor(
}
RoomSettingsViewState.AvatarAction.DeleteAvatar -> {
/* Should not happen */
Unit
}
is RoomSettingsViewState.AvatarAction.UpdateAvatar -> {
// Cancel the update of the avatar

View File

@ -96,6 +96,7 @@ class RoomUploadsFragment @Inject constructor(
private fun renderRoomSummary(state: RoomUploadsViewState) {
state.roomSummary()?.let {
views.roomUploadsToolbarTitleView.text = it.displayName
views.roomUploadsDecorationToolbarAvatarImageView.render(it.roomEncryptionTrustLevel)
avatarRenderer.render(it.toMatrixItem(), views.roomUploadsToolbarAvatarImageView)
}
}

View File

@ -38,7 +38,7 @@ abstract class UploadsImageItem : VectorEpoxyModel<UploadsImageItem.Holder>() {
override fun bind(holder: Holder) {
super.bind(holder)
holder.view.setOnClickListener(
DebouncedClickListener(View.OnClickListener { _ ->
DebouncedClickListener({
listener?.onItemClicked(holder.imageView, data)
})
)

View File

@ -39,7 +39,7 @@ abstract class UploadsVideoItem : VectorEpoxyModel<UploadsVideoItem.Holder>() {
override fun bind(holder: Holder) {
super.bind(holder)
holder.view.setOnClickListener(
DebouncedClickListener(View.OnClickListener { _ ->
DebouncedClickListener({
listener?.onItemClicked(holder.imageView, data)
})
)

View File

@ -361,7 +361,7 @@ class VectorSettingsGeneralFragment @Inject constructor(
views.changePasswordNewPwdText.showPassword(passwordShown)
views.changePasswordConfirmNewPwdText.showPassword(passwordShown)
views.changePasswordShowPasswords.setImageResource(if (passwordShown) R.drawable.ic_eye_closed else R.drawable.ic_eye)
views.changePasswordShowPasswords.render(passwordShown)
}
val dialog = AlertDialog.Builder(activity)

View File

@ -458,7 +458,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
views.importDialogShowPassword.setOnClickListener {
passwordVisible = !passwordVisible
views.dialogE2eKeysPassphraseEditText.showPassword(passwordVisible)
views.importDialogShowPassword.setImageResource(if (passwordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
views.importDialogShowPassword.render(passwordVisible)
}
views.dialogE2eKeysPassphraseEditText.addTextChangedListener(object : SimpleTextWatcher() {

View File

@ -29,7 +29,6 @@ import im.vector.app.core.resources.StringProvider
import im.vector.app.features.auth.ReAuthActivity
import im.vector.app.features.login.ReAuthHelper
import io.reactivex.Observable
import io.reactivex.functions.BiFunction
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.auth.UIABaseAuth
@ -62,7 +61,7 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(
Observable.combineLatest<List<DeviceInfo>, Optional<MXCrossSigningInfo>, Pair<List<DeviceInfo>, Optional<MXCrossSigningInfo>>>(
session.rx().liveMyDevicesInfo(),
session.rx().liveCrossSigningInfo(session.myUserId),
BiFunction { myDevicesInfo, mxCrossSigningInfo ->
{ myDevicesInfo, mxCrossSigningInfo ->
myDevicesInfo to mxCrossSigningInfo
}
)

View File

@ -18,7 +18,6 @@ package im.vector.app.features.settings.devices
import android.graphics.Typeface
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
@ -27,6 +26,7 @@ import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.ui.views.ShieldImageView
import im.vector.app.core.utils.DimensionConverter
import me.gujun.android.span.span
import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel
@ -75,6 +75,7 @@ abstract class DeviceItem : VectorEpoxyModel<DeviceItem.Holder>() {
super.bind(holder)
holder.root.setOnClickListener { itemClickAction?.invoke() }
if (e2eCapable) {
val shield = TrustUtils.shieldForTrust(
currentDevice,
trustedSession,
@ -82,10 +83,9 @@ abstract class DeviceItem : VectorEpoxyModel<DeviceItem.Holder>() {
trusted
)
if (e2eCapable) {
holder.trustIcon.setImageResource(shield)
holder.trustIcon.render(shield)
} else {
holder.trustIcon.setImageDrawable(null)
holder.trustIcon.render(null)
}
val detailedModeLabels = listOf(
@ -152,6 +152,6 @@ abstract class DeviceItem : VectorEpoxyModel<DeviceItem.Holder>() {
val deviceLastSeenLabelText by bind<TextView>(R.id.itemDeviceLastSeenLabel)
val deviceLastSeenText by bind<TextView>(R.id.itemDeviceLastSeen)
val trustIcon by bind<ImageView>(R.id.itemDeviceTrustLevelIcon)
val trustIcon by bind<ShieldImageView>(R.id.itemDeviceTrustLevelIcon)
}
}

View File

@ -24,6 +24,7 @@ import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.list.GenericItem
import im.vector.app.core.ui.list.genericFooterItem
import im.vector.app.core.ui.list.genericItem
import im.vector.app.core.ui.views.toDrawableRes
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
@ -62,7 +63,7 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
trustMSK = data.accountCrossSigningIsTrusted,
legacyMode = !data.hasAccountCrossSigning,
deviceTrustLevel = cryptoDeviceInfo.trustLevel
)
).toDrawableRes()
if (data.hasAccountCrossSigning) {
// Cross Signing is enabled

View File

@ -35,7 +35,6 @@ import im.vector.app.core.resources.StringProvider
import im.vector.app.features.auth.ReAuthActivity
import im.vector.app.features.login.ReAuthHelper
import io.reactivex.Observable
import io.reactivex.functions.BiFunction
import io.reactivex.subjects.PublishSubject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -121,7 +120,7 @@ class DevicesViewModel @AssistedInject constructor(
Observable.combineLatest<List<CryptoDeviceInfo>, List<DeviceInfo>, List<DeviceFullInfo>>(
session.rx().liveUserCryptoDevices(session.myUserId),
session.rx().liveMyDevicesInfo(),
BiFunction { cryptoList, infoList ->
{ cryptoList, infoList ->
infoList
.sortedByDescending { it.lastSeenTs }
.map { deviceInfo ->
@ -239,7 +238,6 @@ class DevicesViewModel @AssistedInject constructor(
uiaContinuation?.resumeWith(Result.failure((Exception())))
uiaContinuation = null
pendingAuth = null
Unit
}
}
}

View File

@ -16,38 +16,50 @@
package im.vector.app.features.settings.devices
import androidx.annotation.DrawableRes
import im.vector.app.R
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel
object TrustUtils {
@DrawableRes
fun shieldForTrust(currentDevice: Boolean, trustMSK: Boolean, legacyMode: Boolean, deviceTrustLevel: DeviceTrustLevel?): Int {
fun shieldForTrust(currentDevice: Boolean,
trustMSK: Boolean,
legacyMode: Boolean,
deviceTrustLevel: DeviceTrustLevel?): RoomEncryptionTrustLevel {
return when {
currentDevice -> {
if (legacyMode) {
// In legacy, current session is always trusted
R.drawable.ic_shield_trusted
RoomEncryptionTrustLevel.Trusted
} else {
// If current session doesn't trust MSK, show red shield for current device
R.drawable.ic_shield_trusted.takeIf { trustMSK } ?: R.drawable.ic_shield_warning
if (trustMSK) {
RoomEncryptionTrustLevel.Trusted
} else {
RoomEncryptionTrustLevel.Warning
}
}
}
else -> {
if (legacyMode) {
// use local trust
R.drawable.ic_shield_trusted.takeIf { deviceTrustLevel?.locallyVerified == true } ?: R.drawable.ic_shield_warning
if (deviceTrustLevel?.locallyVerified == true) {
RoomEncryptionTrustLevel.Trusted
} else {
RoomEncryptionTrustLevel.Warning
}
} else {
if (trustMSK) {
// use cross sign trust, put locally trusted in black
R.drawable.ic_shield_trusted.takeIf { deviceTrustLevel?.crossSigningVerified == true }
?: R.drawable.ic_shield_black.takeIf { deviceTrustLevel?.locallyVerified == true }
?: R.drawable.ic_shield_warning
when {
deviceTrustLevel?.crossSigningVerified == true -> RoomEncryptionTrustLevel.Trusted
deviceTrustLevel?.locallyVerified == true -> RoomEncryptionTrustLevel.Default
else -> RoomEncryptionTrustLevel.Warning
}
} else {
// The current session is untrusted, so displays others in black
// as we can't know the cross-signing state
R.drawable.ic_shield_black
RoomEncryptionTrustLevel.Default
}
}
}

View File

@ -68,7 +68,7 @@ class AccountDataEpoxyController @Inject constructor(
genericItemWithValue {
id(accountData.type)
title(accountData.type)
itemClickAction(DebouncedClickListener(View.OnClickListener {
itemClickAction(DebouncedClickListener({
interactionListener?.didTap(accountData)
}))
itemLongClickAction(View.OnLongClickListener {

View File

@ -67,7 +67,7 @@ class KeyRequestsFragment @Inject constructor(
override fun onPageScrollStateChanged(state: Int) {
childFragmentManager.fragments.forEach {
setHasOptionsMenu(state == SCROLL_STATE_IDLE)
it.setHasOptionsMenu(state == SCROLL_STATE_IDLE)
}
invalidateOptionsMenu()
}

View File

@ -59,10 +59,13 @@ abstract class PushRuleItem : EpoxyModelWithHolder<PushRuleItem.Holder>() {
if (notifAction.shouldNotify && !notifAction.soundName.isNullOrBlank()) {
holder.actionIcon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_action_notify_noisy))
holder.actionIcon.contentDescription = context.getString(R.string.a11y_rule_notify_noisy)
} else if (notifAction.shouldNotify) {
holder.actionIcon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_action_notify_silent))
holder.actionIcon.contentDescription = context.getString(R.string.a11y_rule_notify_silent)
} else {
holder.actionIcon.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_action_dont_notify))
holder.actionIcon.contentDescription = context.getString(R.string.a11y_rule_notify_off)
}
val description = StringBuffer()

View File

@ -98,7 +98,7 @@ class NotificationTroubleshootRecyclerViewAdapter(val tests: ArrayList<Troublesh
val quickFix = test.quickFix
if (quickFix != null) {
troubleshootTestButton.setText(test.quickFix!!.title)
troubleshootTestButton.setOnClickListener { _ ->
troubleshootTestButton.setOnClickListener {
test.quickFix!!.doFix()
}
troubleshootTestButton.visibility = View.VISIBLE

View File

@ -121,7 +121,7 @@ class IncomingShareFragment @Inject constructor(
return true
}
})
views.sendShareButton.setOnClickListener { _ ->
views.sendShareButton.setOnClickListener {
handleSendShare()
}
}

View File

@ -19,7 +19,6 @@ package im.vector.app.features.signout.soft.epoxy
import android.os.Build
import android.text.Editable
import android.widget.Button
import android.widget.ImageView
import androidx.autofill.HintConstants
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
@ -31,6 +30,7 @@ import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.extensions.showPassword
import im.vector.app.core.platform.SimpleTextWatcher
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.views.RevealPasswordImageView
@EpoxyModelClass(layout = R.layout.item_login_password_form)
abstract class LoginPasswordFormItem : VectorEpoxyModel<LoginPasswordFormItem.Holder>() {
@ -76,20 +76,13 @@ abstract class LoginPasswordFormItem : VectorEpoxyModel<LoginPasswordFormItem.Ho
private fun renderPasswordField(holder: Holder) {
holder.passwordField.showPassword(passwordShown)
if (passwordShown) {
holder.passwordReveal.setImageResource(R.drawable.ic_eye_closed)
holder.passwordReveal.contentDescription = stringProvider.getString(R.string.a11y_hide_password)
} else {
holder.passwordReveal.setImageResource(R.drawable.ic_eye)
holder.passwordReveal.contentDescription = stringProvider.getString(R.string.a11y_show_password)
}
holder.passwordReveal.render(passwordShown)
}
class Holder : VectorEpoxyHolder() {
val passwordField by bind<TextInputEditText>(R.id.itemLoginPasswordFormPasswordField)
val passwordFieldTil by bind<TextInputLayout>(R.id.itemLoginPasswordFormPasswordFieldTil)
val passwordReveal by bind<ImageView>(R.id.itemLoginPasswordFormPasswordReveal)
val passwordReveal by bind<RevealPasswordImageView>(R.id.itemLoginPasswordFormPasswordReveal)
val forgetPassword by bind<Button>(R.id.itemLoginPasswordFormForgetPasswordButton)
val submit by bind<Button>(R.id.itemLoginPasswordFormSubmit)
}

View File

@ -49,7 +49,7 @@ import timber.log.Timber
import javax.net.ssl.HttpsURLConnection
class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: WidgetViewState,
private val widgetPostAPIHandlerFactory: WidgetPostAPIHandler.Factory,
widgetPostAPIHandlerFactory: WidgetPostAPIHandler.Factory,
private val stringProvider: StringProvider,
private val session: Session)
: VectorViewModel<WidgetViewState, WidgetAction, WidgetViewEvents>(initialState),

View File

@ -150,6 +150,7 @@
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:adjustViewBounds="true"
android:contentDescription="@string/a11y_screenshot"
android:maxWidth="260dp"
android:scaleType="fitCenter"
tools:src="@tools:sample/backgrounds/scenic" />

View File

@ -16,6 +16,7 @@
android:id="@+id/bgCallView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
tools:src="@tools:sample/avatars" />
@ -53,6 +54,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="?attr/selectableItemBackground"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
tools:src="@tools:sample/avatars" />
@ -61,6 +63,7 @@
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center"
android:importantForAccessibility="no"
android:src="@drawable/ic_call_small_pause" />
</FrameLayout>
@ -70,6 +73,7 @@
android:layout_width="80dp"
android:layout_height="80dp"
android:contentDescription="@string/avatar"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
@ -81,13 +85,13 @@
android:id="@+id/smallIsHeldIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:importantForAccessibility="no"
android:src="@drawable/ic_call_small_pause"
app:layout_constraintBottom_toBottomOf="@id/otherMemberAvatar"
app:layout_constraintEnd_toEndOf="@id/otherMemberAvatar"
app:layout_constraintStart_toStartOf="@id/otherMemberAvatar"
app:layout_constraintTop_toTopOf="@id/otherMemberAvatar" />
<TextView
android:id="@+id/participantNameText"
android:layout_width="0dp"

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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/callDialPad"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -27,10 +28,12 @@
android:id="@+id/callDialPadClose"
android:layout_width="@dimen/layout_touch_size"
android:layout_height="@dimen/layout_touch_size"
android:scaleType="center"
app:tint="?riotx_text_primary"
android:contentDescription="@string/action_close"
android:foreground="?selectableItemBackground"
android:src="@drawable/ic_cross" />
android:scaleType="center"
android:src="@drawable/ic_cross"
app:tint="?riotx_text_primary"
tools:ignore="MissingPrefix" />
</LinearLayout>

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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/callControlsWrapper"
android:layout_width="match_parent"
android:layout_height="match_parent"

View File

@ -47,6 +47,7 @@
android:id="@+id/backupCompleteImage"
android:layout_width="20dp"
android:layout_height="20dp"
android:importantForAccessibility="no"
android:visibility="gone"
app:srcCompat="@drawable/unit_test_ok"
tools:visibility="visible" />

View File

@ -22,6 +22,7 @@
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginTop="@dimen/layout_vertical_margin_big"
android:contentDescription="@string/avatar"
android:elevation="4dp"
android:transitionName="profile"
app:layout_constraintEnd_toEndOf="parent"

View File

@ -45,6 +45,7 @@
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:contentDescription="@string/avatar"
tools:src="@tools:sample/avatars" />
<LinearLayout

Some files were not shown because too many files have changed in this diff Show More