Merge pull request #4837 from vector-im/feature/bma/safe_epoxy_char_sequence

Safe epoxy char sequence
This commit is contained in:
Benoit Marty 2022-01-04 09:53:49 +01:00 committed by GitHub
commit 279f9e00a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
76 changed files with 307 additions and 251 deletions

1
changelog.d/4837.bugfix Normal file
View File

@ -0,0 +1 @@
Stop using CharSequence as EpoxyAttribute because it can lead to crash if the CharSequence mutates during rendering.

View File

@ -108,7 +108,6 @@ class SpanUtilsTest : InstrumentedTest {
val string = SpannableString("Text") val string = SpannableString("Text")
val result = spanUtils.getBindingOptions(string) val result = spanUtils.getBindingOptions(string)
result.canUseTextFuture shouldBeEqualTo true result.canUseTextFuture shouldBeEqualTo true
result.preventMutation shouldBeEqualTo false
} }
@Test @Test
@ -117,7 +116,6 @@ class SpanUtilsTest : InstrumentedTest {
string.setSpan(StrikethroughSpan(), 10, 23, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) string.setSpan(StrikethroughSpan(), 10, 23, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
val result = spanUtils.getBindingOptions(string) val result = spanUtils.getBindingOptions(string)
result.canUseTextFuture shouldBeEqualTo false result.canUseTextFuture shouldBeEqualTo false
result.preventMutation shouldBeEqualTo false
} }
@Test @Test
@ -125,7 +123,6 @@ class SpanUtilsTest : InstrumentedTest {
val string = SpannableString("Emoji \uD83D\uDE2E\u200D\uD83D\uDCA8") val string = SpannableString("Emoji \uD83D\uDE2E\u200D\uD83D\uDCA8")
val result = spanUtils.getBindingOptions(string) val result = spanUtils.getBindingOptions(string)
result.canUseTextFuture shouldBeEqualTo false result.canUseTextFuture shouldBeEqualTo false
result.preventMutation shouldBeEqualTo true
} }
private fun trueIfAlwaysAllowed() = Build.VERSION.SDK_INT < Build.VERSION_CODES.P private fun trueIfAlwaysAllowed() = Build.VERSION.SDK_INT < Build.VERSION_CODES.P

View File

@ -26,15 +26,14 @@ import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.charsequence.EpoxyCharSequence
import im.vector.app.core.epoxy.onClick import im.vector.app.core.epoxy.onClick
import im.vector.app.core.epoxy.util.preventMutation
import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.features.displayname.getBestName import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.timeline.item.BindingOptions import im.vector.app.features.home.room.detail.timeline.item.BindingOptions
import im.vector.app.features.home.room.detail.timeline.tools.findPillsAndProcess import im.vector.app.features.home.room.detail.timeline.tools.findPillsAndProcess
import im.vector.app.features.media.ImageContentRenderer import im.vector.app.features.media.ImageContentRenderer
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.MatrixItem
/** /**
@ -50,13 +49,13 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
lateinit var matrixItem: MatrixItem lateinit var matrixItem: MatrixItem
@EpoxyAttribute @EpoxyAttribute
lateinit var body: CharSequence lateinit var body: EpoxyCharSequence
@EpoxyAttribute @EpoxyAttribute
var bindingOptions: BindingOptions? = null var bindingOptions: BindingOptions? = null
@EpoxyAttribute @EpoxyAttribute
var bodyDetails: CharSequence? = null var bodyDetails: EpoxyCharSequence? = null
@EpoxyAttribute @EpoxyAttribute
var imageContentRenderer: ImageContentRenderer? = null var imageContentRenderer: ImageContentRenderer? = null
@ -65,7 +64,7 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
var data: ImageContentRenderer.Data? = null var data: ImageContentRenderer.Data? = null
@EpoxyAttribute @EpoxyAttribute
var time: CharSequence? = null var time: String? = null
@EpoxyAttribute @EpoxyAttribute
var movementMethod: MovementMethod? = null var movementMethod: MovementMethod? = null
@ -84,13 +83,9 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
} }
holder.imagePreview.isVisible = data != null holder.imagePreview.isVisible = data != null
holder.body.movementMethod = movementMethod holder.body.movementMethod = movementMethod
holder.body.text = if (bindingOptions?.preventMutation.orFalse()) { holder.body.text = body.charSequence
body.preventMutation() holder.bodyDetails.setTextOrHide(bodyDetails?.charSequence)
} else { body.charSequence.findPillsAndProcess(coroutineScope) { it.bind(holder.body) }
body
}
holder.bodyDetails.setTextOrHide(bodyDetails)
body.findPillsAndProcess(coroutineScope) { it.bind(holder.body) }
holder.timestamp.setTextOrHide(time) holder.timestamp.setTextOrHide(time)
} }

View File

@ -37,7 +37,7 @@ import im.vector.app.core.extensions.setTextOrHide
abstract class BottomSheetRadioActionItem : VectorEpoxyModel<BottomSheetRadioActionItem.Holder>() { abstract class BottomSheetRadioActionItem : VectorEpoxyModel<BottomSheetRadioActionItem.Holder>() {
@EpoxyAttribute @EpoxyAttribute
var title: CharSequence? = null var title: String? = null
@StringRes @StringRes
@EpoxyAttribute @EpoxyAttribute
@ -47,7 +47,7 @@ abstract class BottomSheetRadioActionItem : VectorEpoxyModel<BottomSheetRadioAct
var selected = false var selected = false
@EpoxyAttribute @EpoxyAttribute
var description: CharSequence? = null var description: String? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
lateinit var listener: ClickListener lateinit var listener: ClickListener

View File

@ -36,7 +36,7 @@ abstract class BottomSheetSendStateItem : VectorEpoxyModel<BottomSheetSendStateI
var showProgress: Boolean = false var showProgress: Boolean = false
@EpoxyAttribute @EpoxyAttribute
lateinit var text: CharSequence lateinit var text: String
@EpoxyAttribute @EpoxyAttribute
@DrawableRes @DrawableRes

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.core.epoxy.charsequence
/**
* Wrapper for a CharSequence, which support mutation of the CharSequence, which can happen during rendering
*/
class EpoxyCharSequence(val charSequence: CharSequence) {
private val hash = charSequence.toString().hashCode()
override fun hashCode() = hash
override fun equals(other: Any?) = other is EpoxyCharSequence && other.hash == hash
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2021 New Vector Ltd * Copyright (c) 2022 New Vector Ltd
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,8 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.core.epoxy.util package im.vector.app.core.epoxy.charsequence
import android.text.SpannableString /**
* Extensions to wrap CharSequence to EpoxyCharSequence
fun CharSequence?.preventMutation(): CharSequence? = this?.let { SpannableString(it) } */
fun CharSequence.toEpoxyCharSequence() = EpoxyCharSequence(this)

View File

@ -33,7 +33,7 @@ import im.vector.app.core.extensions.setAttributeTintedImageResource
abstract class RadioButtonItem : VectorEpoxyModel<RadioButtonItem.Holder>() { abstract class RadioButtonItem : VectorEpoxyModel<RadioButtonItem.Holder>() {
@EpoxyAttribute @EpoxyAttribute
var title: CharSequence? = null var title: String? = null
@StringRes @StringRes
@EpoxyAttribute @EpoxyAttribute

View File

@ -23,7 +23,7 @@ import im.vector.app.core.platform.VectorSharedAction
* Parent class for a bottom sheet action * Parent class for a bottom sheet action
*/ */
open class BottomSheetGenericRadioAction( open class BottomSheetGenericRadioAction(
open val title: CharSequence?, open val title: String?,
open val description: String? = null, open val description: String? = null,
open val isSelected: Boolean open val isSelected: Boolean
) : VectorSharedAction { ) : VectorSharedAction {

View File

@ -39,10 +39,10 @@ import im.vector.app.core.extensions.setTextOrHide
abstract class GenericEmptyWithActionItem : VectorEpoxyModel<GenericEmptyWithActionItem.Holder>() { abstract class GenericEmptyWithActionItem : VectorEpoxyModel<GenericEmptyWithActionItem.Holder>() {
@EpoxyAttribute @EpoxyAttribute
var title: CharSequence? = null var title: String? = null
@EpoxyAttribute @EpoxyAttribute
var description: CharSequence? = null var description: String? = null
@EpoxyAttribute @EpoxyAttribute
@DrawableRes @DrawableRes

View File

@ -38,7 +38,7 @@ import im.vector.app.features.themes.ThemeUtils
abstract class GenericFooterItem : VectorEpoxyModel<GenericFooterItem.Holder>() { abstract class GenericFooterItem : VectorEpoxyModel<GenericFooterItem.Holder>() {
@EpoxyAttribute @EpoxyAttribute
var text: CharSequence? = null var text: String? = null
@EpoxyAttribute @EpoxyAttribute
var style: ItemStyle = ItemStyle.NORMAL_TEXT var style: ItemStyle = ItemStyle.NORMAL_TEXT

View File

@ -28,6 +28,7 @@ import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.charsequence.EpoxyCharSequence
import im.vector.app.core.epoxy.onClick import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.setTextOrHide
@ -41,10 +42,10 @@ import im.vector.app.core.extensions.setTextOrHide
abstract class GenericItem : VectorEpoxyModel<GenericItem.Holder>() { abstract class GenericItem : VectorEpoxyModel<GenericItem.Holder>() {
@EpoxyAttribute @EpoxyAttribute
var title: CharSequence? = null var title: EpoxyCharSequence? = null
@EpoxyAttribute @EpoxyAttribute
var description: CharSequence? = null var description: EpoxyCharSequence? = null
@EpoxyAttribute @EpoxyAttribute
var style: ItemStyle = ItemStyle.NORMAL_TEXT var style: ItemStyle = ItemStyle.NORMAL_TEXT
@ -71,7 +72,7 @@ abstract class GenericItem : VectorEpoxyModel<GenericItem.Holder>() {
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
holder.titleText.setTextOrHide(title) holder.titleText.setTextOrHide(title?.charSequence)
if (titleIconResourceId != -1) { if (titleIconResourceId != -1) {
holder.titleIcon.setImageResource(titleIconResourceId) holder.titleIcon.setImageResource(titleIconResourceId)
@ -82,7 +83,7 @@ abstract class GenericItem : VectorEpoxyModel<GenericItem.Holder>() {
holder.titleText.textSize = style.toTextSize() holder.titleText.textSize = style.toTextSize()
holder.descriptionText.setTextOrHide(description) holder.descriptionText.setTextOrHide(description?.charSequence)
if (hasIndeterminateProcess) { if (hasIndeterminateProcess) {
holder.progressBar.isVisible = true holder.progressBar.isVisible = true

View File

@ -28,6 +28,7 @@ import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.charsequence.EpoxyCharSequence
import im.vector.app.core.epoxy.onClick import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
@ -39,7 +40,7 @@ import im.vector.app.features.themes.ThemeUtils
abstract class GenericPillItem : VectorEpoxyModel<GenericPillItem.Holder>() { abstract class GenericPillItem : VectorEpoxyModel<GenericPillItem.Holder>() {
@EpoxyAttribute @EpoxyAttribute
var text: CharSequence? = null var text: EpoxyCharSequence? = null
@EpoxyAttribute @EpoxyAttribute
var style: ItemStyle = ItemStyle.NORMAL_TEXT var style: ItemStyle = ItemStyle.NORMAL_TEXT
@ -60,7 +61,7 @@ abstract class GenericPillItem : VectorEpoxyModel<GenericPillItem.Holder>() {
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
holder.textView.setTextOrHide(text) holder.textView.setTextOrHide(text?.charSequence)
holder.textView.typeface = style.toTypeFace() holder.textView.typeface = style.toTypeFace()
holder.textView.textSize = style.toTextSize() holder.textView.textSize = style.toTextSize()
holder.textView.gravity = if (centered) Gravity.CENTER_HORIZONTAL else Gravity.START holder.textView.gravity = if (centered) Gravity.CENTER_HORIZONTAL else Gravity.START

View File

@ -27,6 +27,7 @@ import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.charsequence.EpoxyCharSequence
import im.vector.app.core.epoxy.onClick import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
@ -41,10 +42,10 @@ import im.vector.app.features.themes.ThemeUtils
abstract class GenericWithValueItem : VectorEpoxyModel<GenericWithValueItem.Holder>() { abstract class GenericWithValueItem : VectorEpoxyModel<GenericWithValueItem.Holder>() {
@EpoxyAttribute @EpoxyAttribute
var title: CharSequence? = null var title: EpoxyCharSequence? = null
@EpoxyAttribute @EpoxyAttribute
var value: CharSequence? = null var value: String? = null
@EpoxyAttribute @EpoxyAttribute
@ColorInt @ColorInt
@ -62,7 +63,7 @@ abstract class GenericWithValueItem : VectorEpoxyModel<GenericWithValueItem.Hold
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
holder.titleText.setTextOrHide(title) holder.titleText.setTextOrHide(title?.charSequence)
if (titleIconResourceId != -1) { if (titleIconResourceId != -1) {
holder.titleIcon.setImageResource(titleIconResourceId) holder.titleIcon.setImageResource(titleIconResourceId)

View File

@ -29,13 +29,13 @@ import im.vector.app.core.epoxy.onClick
abstract class AutocompleteCommandItem : VectorEpoxyModel<AutocompleteCommandItem.Holder>() { abstract class AutocompleteCommandItem : VectorEpoxyModel<AutocompleteCommandItem.Holder>() {
@EpoxyAttribute @EpoxyAttribute
var name: CharSequence? = null var name: String? = null
@EpoxyAttribute @EpoxyAttribute
var parameters: CharSequence? = null var parameters: String? = null
@EpoxyAttribute @EpoxyAttribute
var description: CharSequence? = null var description: String? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var clickListener: ClickListener? = null var clickListener: ClickListener? = null

View File

@ -22,6 +22,7 @@ import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.Uninitialized
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.epoxy.errorWithRetryItem import im.vector.app.core.epoxy.errorWithRetryItem
import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.loadingItem
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
@ -73,11 +74,11 @@ class KeysBackupSettingsRecyclerViewController @Inject constructor(
KeysBackupState.Disabled -> { KeysBackupState.Disabled -> {
genericItem { genericItem {
id("summary") id("summary")
title(host.stringProvider.getString(R.string.keys_backup_settings_status_not_setup)) title(host.stringProvider.getString(R.string.keys_backup_settings_status_not_setup).toEpoxyCharSequence())
style(ItemStyle.BIG_TEXT) style(ItemStyle.BIG_TEXT)
if (data.keysBackupVersionTrust()?.usable == false) { if (data.keysBackupVersionTrust()?.usable == false) {
description(host.stringProvider.getString(R.string.keys_backup_settings_untrusted_backup)) description(host.stringProvider.getString(R.string.keys_backup_settings_untrusted_backup).toEpoxyCharSequence())
} }
} }
@ -88,12 +89,12 @@ class KeysBackupSettingsRecyclerViewController @Inject constructor(
KeysBackupState.Enabling -> { KeysBackupState.Enabling -> {
genericItem { genericItem {
id("summary") id("summary")
title(host.stringProvider.getString(R.string.keys_backup_settings_status_ko)) title(host.stringProvider.getString(R.string.keys_backup_settings_status_ko).toEpoxyCharSequence())
style(ItemStyle.BIG_TEXT) style(ItemStyle.BIG_TEXT)
if (data.keysBackupVersionTrust()?.usable == false) { if (data.keysBackupVersionTrust()?.usable == false) {
description(host.stringProvider.getString(R.string.keys_backup_settings_untrusted_backup)) description(host.stringProvider.getString(R.string.keys_backup_settings_untrusted_backup).toEpoxyCharSequence())
} else { } else {
description(keyBackupState.toString()) description(keyBackupState.toString().toEpoxyCharSequence())
} }
endIconResourceId(R.drawable.unit_test_ko) endIconResourceId(R.drawable.unit_test_ko)
} }
@ -103,12 +104,12 @@ class KeysBackupSettingsRecyclerViewController @Inject constructor(
KeysBackupState.ReadyToBackUp -> { KeysBackupState.ReadyToBackUp -> {
genericItem { genericItem {
id("summary") id("summary")
title(host.stringProvider.getString(R.string.keys_backup_settings_status_ok)) title(host.stringProvider.getString(R.string.keys_backup_settings_status_ok).toEpoxyCharSequence())
style(ItemStyle.BIG_TEXT) style(ItemStyle.BIG_TEXT)
if (data.keysBackupVersionTrust()?.usable == false) { if (data.keysBackupVersionTrust()?.usable == false) {
description(host.stringProvider.getString(R.string.keys_backup_settings_untrusted_backup)) description(host.stringProvider.getString(R.string.keys_backup_settings_untrusted_backup).toEpoxyCharSequence())
} else { } else {
description(host.stringProvider.getString(R.string.keys_backup_info_keys_all_backup_up)) description(host.stringProvider.getString(R.string.keys_backup_info_keys_all_backup_up).toEpoxyCharSequence())
} }
endIconResourceId(R.drawable.unit_test_ok) endIconResourceId(R.drawable.unit_test_ok)
} }
@ -119,7 +120,7 @@ class KeysBackupSettingsRecyclerViewController @Inject constructor(
KeysBackupState.BackingUp -> { KeysBackupState.BackingUp -> {
genericItem { genericItem {
id("summary") id("summary")
title(host.stringProvider.getString(R.string.keys_backup_settings_status_ok)) title(host.stringProvider.getString(R.string.keys_backup_settings_status_ok).toEpoxyCharSequence())
style(ItemStyle.BIG_TEXT) style(ItemStyle.BIG_TEXT)
hasIndeterminateProcess(true) hasIndeterminateProcess(true)
@ -129,10 +130,11 @@ class KeysBackupSettingsRecyclerViewController @Inject constructor(
val remainingKeysToBackup = totalKeys - backedUpKeys val remainingKeysToBackup = totalKeys - backedUpKeys
if (data.keysBackupVersionTrust()?.usable == false) { if (data.keysBackupVersionTrust()?.usable == false) {
description(host.stringProvider.getString(R.string.keys_backup_settings_untrusted_backup)) description(host.stringProvider.getString(R.string.keys_backup_settings_untrusted_backup).toEpoxyCharSequence())
} else { } else {
description(host.stringProvider description(host.stringProvider
.getQuantityString(R.plurals.keys_backup_info_keys_backing_up, remainingKeysToBackup, remainingKeysToBackup)) .getQuantityString(R.plurals.keys_backup_info_keys_backing_up, remainingKeysToBackup, remainingKeysToBackup)
.toEpoxyCharSequence())
} }
} }
@ -144,14 +146,14 @@ class KeysBackupSettingsRecyclerViewController @Inject constructor(
// Add infos // Add infos
genericItem { genericItem {
id("version") id("version")
title(host.stringProvider.getString(R.string.keys_backup_info_title_version)) title(host.stringProvider.getString(R.string.keys_backup_info_title_version).toEpoxyCharSequence())
description(keyVersionResult?.version ?: "") description(keyVersionResult?.version.orEmpty().toEpoxyCharSequence())
} }
genericItem { genericItem {
id("algorithm") id("algorithm")
title(host.stringProvider.getString(R.string.keys_backup_info_title_algorithm)) title(host.stringProvider.getString(R.string.keys_backup_info_title_algorithm).toEpoxyCharSequence())
description(keyVersionResult?.algorithm ?: "") description(keyVersionResult?.algorithm.orEmpty().toEpoxyCharSequence())
} }
if (vectorPreferences.developerMode()) { if (vectorPreferences.developerMode()) {
@ -189,7 +191,7 @@ class KeysBackupSettingsRecyclerViewController @Inject constructor(
keysVersionTrust().signatures.forEach { keysVersionTrust().signatures.forEach {
genericItem { genericItem {
id(UUID.randomUUID().toString()) id(UUID.randomUUID().toString())
title(host.stringProvider.getString(R.string.keys_backup_info_title_signature)) title(host.stringProvider.getString(R.string.keys_backup_info_title_signature).toEpoxyCharSequence())
val isDeviceKnown = it.device != null val isDeviceKnown = it.device != null
val isDeviceVerified = it.device?.isVerified ?: false val isDeviceVerified = it.device?.isVerified ?: false
@ -197,21 +199,27 @@ class KeysBackupSettingsRecyclerViewController @Inject constructor(
val deviceId: String = it.deviceId ?: "" val deviceId: String = it.deviceId ?: ""
if (!isDeviceKnown) { if (!isDeviceKnown) {
description(host.stringProvider.getString(R.string.keys_backup_settings_signature_from_unknown_device, deviceId)) description(host.stringProvider
.getString(R.string.keys_backup_settings_signature_from_unknown_device, deviceId)
.toEpoxyCharSequence())
endIconResourceId(R.drawable.e2e_warning) endIconResourceId(R.drawable.e2e_warning)
} else { } else {
if (isSignatureValid) { if (isSignatureValid) {
if (host.session.sessionParams.deviceId == it.deviceId) { if (host.session.sessionParams.deviceId == it.deviceId) {
description(host.stringProvider.getString(R.string.keys_backup_settings_valid_signature_from_this_device)) description(host.stringProvider
.getString(R.string.keys_backup_settings_valid_signature_from_this_device)
.toEpoxyCharSequence())
endIconResourceId(R.drawable.e2e_verified) endIconResourceId(R.drawable.e2e_verified)
} else { } else {
if (isDeviceVerified) { if (isDeviceVerified) {
description(host.stringProvider description(host.stringProvider
.getString(R.string.keys_backup_settings_valid_signature_from_verified_device, deviceId)) .getString(R.string.keys_backup_settings_valid_signature_from_verified_device, deviceId)
.toEpoxyCharSequence())
endIconResourceId(R.drawable.e2e_verified) endIconResourceId(R.drawable.e2e_verified)
} else { } else {
description(host.stringProvider description(host.stringProvider
.getString(R.string.keys_backup_settings_valid_signature_from_unverified_device, deviceId)) .getString(R.string.keys_backup_settings_valid_signature_from_unverified_device, deviceId)
.toEpoxyCharSequence())
endIconResourceId(R.drawable.e2e_warning) endIconResourceId(R.drawable.e2e_warning)
} }
} }
@ -220,10 +228,12 @@ class KeysBackupSettingsRecyclerViewController @Inject constructor(
endIconResourceId(R.drawable.e2e_warning) endIconResourceId(R.drawable.e2e_warning)
if (isDeviceVerified) { if (isDeviceVerified) {
description(host.stringProvider description(host.stringProvider
.getString(R.string.keys_backup_settings_invalid_signature_from_verified_device, deviceId)) .getString(R.string.keys_backup_settings_invalid_signature_from_verified_device, deviceId)
.toEpoxyCharSequence())
} else { } else {
description(host.stringProvider description(host.stringProvider
.getString(R.string.keys_backup_settings_invalid_signature_from_unverified_device, deviceId)) .getString(R.string.keys_backup_settings_invalid_signature_from_unverified_device, deviceId)
.toEpoxyCharSequence())
} }
} }
} }

View File

@ -20,6 +20,8 @@ import androidx.core.text.toSpannable
import com.airbnb.epoxy.EpoxyController import com.airbnb.epoxy.EpoxyController
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.epoxy.charsequence.EpoxyCharSequence
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.colorizeMatchingText import im.vector.app.core.utils.colorizeMatchingText
@ -49,12 +51,12 @@ class VerificationCancelController @Inject constructor(
if (state.currentDeviceCanCrossSign) { if (state.currentDeviceCanCrossSign) {
bottomSheetVerificationNoticeItem { bottomSheetVerificationNoticeItem {
id("notice") id("notice")
notice(host.stringProvider.getString(R.string.verify_cancel_self_verification_from_trusted)) notice(host.stringProvider.getString(R.string.verify_cancel_self_verification_from_trusted).toEpoxyCharSequence())
} }
} else { } else {
bottomSheetVerificationNoticeItem { bottomSheetVerificationNoticeItem {
id("notice") id("notice")
notice(host.stringProvider.getString(R.string.verify_cancel_self_verification_from_untrusted)) notice(host.stringProvider.getString(R.string.verify_cancel_self_verification_from_untrusted).toEpoxyCharSequence())
} }
} }
} else { } else {
@ -63,9 +65,11 @@ class VerificationCancelController @Inject constructor(
bottomSheetVerificationNoticeItem { bottomSheetVerificationNoticeItem {
id("notice") id("notice")
notice( notice(
host.stringProvider.getString(R.string.verify_cancel_other, otherDisplayName, otherUserID) EpoxyCharSequence(
.toSpannable() host.stringProvider.getString(R.string.verify_cancel_other, otherDisplayName, otherUserID)
.colorizeMatchingText(otherUserID, host.colorProvider.getColorFromAttribute(R.attr.vctr_notice_text_color)) .toSpannable()
.colorizeMatchingText(otherUserID, host.colorProvider.getColorFromAttribute(R.attr.vctr_notice_text_color))
)
) )
} }
} }

View File

@ -19,6 +19,7 @@ package im.vector.app.features.crypto.verification.cancel
import com.airbnb.epoxy.EpoxyController import com.airbnb.epoxy.EpoxyController
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewState import im.vector.app.features.crypto.verification.VerificationBottomSheetViewState
@ -46,7 +47,7 @@ class VerificationNotMeController @Inject constructor(
val host = this val host = this
bottomSheetVerificationNoticeItem { bottomSheetVerificationNoticeItem {
id("notice") id("notice")
notice(host.eventHtmlRenderer.render(host.stringProvider.getString(R.string.verify_not_me_self_verification))) notice(host.eventHtmlRenderer.render(host.stringProvider.getString(R.string.verify_not_me_self_verification)).toEpoxyCharSequence())
} }
bottomSheetDividerItem { bottomSheetDividerItem {

View File

@ -19,6 +19,7 @@ package im.vector.app.features.crypto.verification.choose
import com.airbnb.epoxy.EpoxyController import com.airbnb.epoxy.EpoxyController
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
@ -60,7 +61,7 @@ class VerificationChooseMethodController @Inject constructor(
bottomSheetVerificationNoticeItem { bottomSheetVerificationNoticeItem {
id("notice") id("notice")
notice(scanCodeInstructions) notice(scanCodeInstructions.toEpoxyCharSequence())
} }
if (state.otherCanScanQrCode && !state.qrCodeText.isNullOrBlank()) { if (state.otherCanScanQrCode && !state.qrCodeText.isNullOrBlank()) {

View File

@ -19,6 +19,7 @@ package im.vector.app.features.crypto.verification.conclusion
import com.airbnb.epoxy.EpoxyController import com.airbnb.epoxy.EpoxyController
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
@ -53,7 +54,8 @@ class VerificationConclusionController @Inject constructor(
id("notice") id("notice")
notice(host.stringProvider.getString( notice(host.stringProvider.getString(
if (state.isSelfVerification) R.string.verification_conclusion_ok_self_notice if (state.isSelfVerification) R.string.verification_conclusion_ok_self_notice
else R.string.verification_conclusion_ok_notice)) else R.string.verification_conclusion_ok_notice)
.toEpoxyCharSequence())
} }
bottomSheetVerificationBigImageItem { bottomSheetVerificationBigImageItem {
@ -66,7 +68,7 @@ class VerificationConclusionController @Inject constructor(
ConclusionState.WARNING -> { ConclusionState.WARNING -> {
bottomSheetVerificationNoticeItem { bottomSheetVerificationNoticeItem {
id("notice") id("notice")
notice(host.stringProvider.getString(R.string.verification_conclusion_not_secure)) notice(host.stringProvider.getString(R.string.verification_conclusion_not_secure).toEpoxyCharSequence())
} }
bottomSheetVerificationBigImageItem { bottomSheetVerificationBigImageItem {
@ -76,7 +78,7 @@ class VerificationConclusionController @Inject constructor(
bottomSheetVerificationNoticeItem { bottomSheetVerificationNoticeItem {
id("warning_notice") id("warning_notice")
notice(host.eventHtmlRenderer.render(host.stringProvider.getString(R.string.verification_conclusion_compromised))) notice(host.eventHtmlRenderer.render(host.stringProvider.getString(R.string.verification_conclusion_compromised)).toEpoxyCharSequence())
} }
bottomDone() bottomDone()
@ -84,7 +86,7 @@ class VerificationConclusionController @Inject constructor(
ConclusionState.CANCELLED -> { ConclusionState.CANCELLED -> {
bottomSheetVerificationNoticeItem { bottomSheetVerificationNoticeItem {
id("notice_cancelled") id("notice_cancelled")
notice(host.stringProvider.getString(R.string.verify_cancelled_notice)) notice(host.stringProvider.getString(R.string.verify_cancelled_notice).toEpoxyCharSequence())
} }
bottomSheetDividerItem { bottomSheetDividerItem {

View File

@ -21,6 +21,7 @@ import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.epoxy.errorWithRetryItem import im.vector.app.core.epoxy.errorWithRetryItem
import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
@ -64,7 +65,7 @@ class VerificationEmojiCodeController @Inject constructor(
is Success -> { is Success -> {
bottomSheetVerificationNoticeItem { bottomSheetVerificationNoticeItem {
id("notice") id("notice")
notice(host.stringProvider.getString(R.string.verification_emoji_notice)) notice(host.stringProvider.getString(R.string.verification_emoji_notice).toEpoxyCharSequence())
} }
bottomSheetVerificationEmojisItem { bottomSheetVerificationEmojisItem {
@ -101,7 +102,7 @@ class VerificationEmojiCodeController @Inject constructor(
is Success -> { is Success -> {
bottomSheetVerificationNoticeItem { bottomSheetVerificationNoticeItem {
id("notice") id("notice")
notice(host.stringProvider.getString(R.string.verification_code_notice)) notice(host.stringProvider.getString(R.string.verification_code_notice).toEpoxyCharSequence())
} }
bottomSheetVerificationDecimalCodeItem { bottomSheetVerificationDecimalCodeItem {

View File

@ -42,10 +42,10 @@ abstract class BottomSheetVerificationActionItem : VectorEpoxyModel<BottomSheetV
var iconRes: Int = -1 var iconRes: Int = -1
@EpoxyAttribute @EpoxyAttribute
var title: CharSequence = "" var title: String = ""
@EpoxyAttribute @EpoxyAttribute
var subTitle: CharSequence? = null var subTitle: String? = null
@EpoxyAttribute @EpoxyAttribute
var titleColor: Int = 0 var titleColor: Int = 0

View File

@ -30,7 +30,7 @@ import im.vector.app.core.epoxy.VectorEpoxyModel
abstract class BottomSheetVerificationDecimalCodeItem : VectorEpoxyModel<BottomSheetVerificationDecimalCodeItem.Holder>() { abstract class BottomSheetVerificationDecimalCodeItem : VectorEpoxyModel<BottomSheetVerificationDecimalCodeItem.Holder>() {
@EpoxyAttribute @EpoxyAttribute
var code: CharSequence = "" var code: String = ""
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)

View File

@ -22,6 +22,7 @@ import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.charsequence.EpoxyCharSequence
/** /**
* A action for bottom sheet. * A action for bottom sheet.
@ -30,11 +31,11 @@ import im.vector.app.core.epoxy.VectorEpoxyModel
abstract class BottomSheetVerificationNoticeItem : VectorEpoxyModel<BottomSheetVerificationNoticeItem.Holder>() { abstract class BottomSheetVerificationNoticeItem : VectorEpoxyModel<BottomSheetVerificationNoticeItem.Holder>() {
@EpoxyAttribute @EpoxyAttribute
var notice: CharSequence = "" lateinit var notice: EpoxyCharSequence
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
holder.notice.text = notice holder.notice.text = notice.charSequence
} }
class Holder : VectorEpoxyHolder() { class Holder : VectorEpoxyHolder() {

View File

@ -30,7 +30,7 @@ import im.vector.app.core.epoxy.VectorEpoxyModel
abstract class BottomSheetVerificationWaitingItem : VectorEpoxyModel<BottomSheetVerificationWaitingItem.Holder>() { abstract class BottomSheetVerificationWaitingItem : VectorEpoxyModel<BottomSheetVerificationWaitingItem.Holder>() {
@EpoxyAttribute @EpoxyAttribute
var title: CharSequence = "" var title: String = ""
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)

View File

@ -18,6 +18,7 @@ package im.vector.app.features.crypto.verification.qrconfirmation
import com.airbnb.epoxy.EpoxyController import com.airbnb.epoxy.EpoxyController
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem
@ -45,7 +46,7 @@ class VerificationQRWaitingController @Inject constructor(
bottomSheetVerificationNoticeItem { bottomSheetVerificationNoticeItem {
id("notice") id("notice")
apply { apply {
notice(host.stringProvider.getString(R.string.qr_code_scanned_verif_waiting_notice)) notice(host.stringProvider.getString(R.string.qr_code_scanned_verif_waiting_notice).toEpoxyCharSequence())
} }
} }

View File

@ -19,6 +19,7 @@ package im.vector.app.features.crypto.verification.qrconfirmation
import com.airbnb.epoxy.EpoxyController import com.airbnb.epoxy.EpoxyController
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewState import im.vector.app.features.crypto.verification.VerificationBottomSheetViewState
@ -51,10 +52,10 @@ class VerificationQrScannedByOtherController @Inject constructor(
id("notice") id("notice")
apply { apply {
if (state.isMe) { if (state.isMe) {
notice(host.stringProvider.getString(R.string.qr_code_scanned_self_verif_notice)) notice(host.stringProvider.getString(R.string.qr_code_scanned_self_verif_notice).toEpoxyCharSequence())
} else { } else {
val name = state.otherUserMxItem?.getBestName() ?: "" val name = state.otherUserMxItem?.getBestName() ?: ""
notice(host.stringProvider.getString(R.string.qr_code_scanned_by_other_notice, name)) notice(host.stringProvider.getString(R.string.qr_code_scanned_by_other_notice, name).toEpoxyCharSequence())
} }
} }
} }

View File

@ -23,6 +23,7 @@ import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.Uninitialized
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.colorizeMatchingText import im.vector.app.core.utils.colorizeMatchingText
@ -57,7 +58,7 @@ class VerificationRequestController @Inject constructor(
if (state.hasAnyOtherSession) { if (state.hasAnyOtherSession) {
bottomSheetVerificationNoticeItem { bottomSheetVerificationNoticeItem {
id("notice") id("notice")
notice(host.stringProvider.getString(R.string.verification_open_other_to_verify)) notice(host.stringProvider.getString(R.string.verification_open_other_to_verify).toEpoxyCharSequence())
} }
bottomSheetSelfWaitItem { bottomSheetSelfWaitItem {
@ -112,7 +113,7 @@ class VerificationRequestController @Inject constructor(
bottomSheetVerificationNoticeItem { bottomSheetVerificationNoticeItem {
id("notice") id("notice")
notice(styledText) notice(styledText.toEpoxyCharSequence())
} }
bottomSheetDividerItem { bottomSheetDividerItem {

View File

@ -18,6 +18,7 @@ package im.vector.app.features.devtools
import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.epoxy.TypedEpoxyController
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.epoxy.noResultItem import im.vector.app.core.epoxy.noResultItem
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
@ -48,8 +49,8 @@ class RoomStateListController @Inject constructor(
stateEventsGroups.forEach { entry -> stateEventsGroups.forEach { entry ->
genericItem { genericItem {
id(entry.key) id(entry.key)
title(entry.key) title(entry.key.toEpoxyCharSequence())
description(host.stringProvider.getQuantityString(R.plurals.entries, entry.value.size, entry.value.size)) description(host.stringProvider.getQuantityString(R.plurals.entries, entry.value.size, entry.value.size).toEpoxyCharSequence())
itemClickAction { itemClickAction {
host.interactionListener?.processAction(RoomDevToolAction.ShowStateEventType(entry.key)) host.interactionListener?.processAction(RoomDevToolAction.ShowStateEventType(entry.key))
} }
@ -88,8 +89,8 @@ class RoomStateListController @Inject constructor(
text = stateEvent.stateKey.let { "\"$it\"" } text = stateEvent.stateKey.let { "\"$it\"" }
textStyle = "normal" textStyle = "normal"
} }
}) }.toEpoxyCharSequence())
description(contentJson) description(contentJson.toEpoxyCharSequence())
itemClickAction { itemClickAction {
host.interactionListener?.processAction(RoomDevToolAction.ShowStateEvent(stateEvent)) host.interactionListener?.processAction(RoomDevToolAction.ShowStateEvent(stateEvent))
} }

View File

@ -43,7 +43,7 @@ abstract class SettingsItem : EpoxyModelWithHolder<SettingsItem.Holder>() {
var descriptionResId: Int? = null var descriptionResId: Int? = null
@EpoxyAttribute @EpoxyAttribute
var description: CharSequence? = null var description: String? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var itemClickListener: ClickListener? = null var itemClickListener: ClickListener? = null

View File

@ -31,7 +31,7 @@ import im.vector.app.features.themes.ThemeUtils
@EpoxyModelClass(layout = R.layout.item_form_advanced_toggle) @EpoxyModelClass(layout = R.layout.item_form_advanced_toggle)
abstract class FormAdvancedToggleItem : VectorEpoxyModel<FormAdvancedToggleItem.Holder>() { abstract class FormAdvancedToggleItem : VectorEpoxyModel<FormAdvancedToggleItem.Holder>() {
@EpoxyAttribute lateinit var title: CharSequence @EpoxyAttribute lateinit var title: String
@EpoxyAttribute var expanded: Boolean = false @EpoxyAttribute var expanded: Boolean = false
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null

View File

@ -39,7 +39,7 @@ abstract class FormSwitchItem : VectorEpoxyModel<FormSwitchItem.Holder>() {
var switchChecked: Boolean = false var switchChecked: Boolean = false
@EpoxyAttribute @EpoxyAttribute
var title: CharSequence? = null var title: String? = null
@EpoxyAttribute @EpoxyAttribute
var summary: String? = null var summary: String? = null

View File

@ -34,7 +34,7 @@ import org.matrix.android.sdk.api.util.MatrixItem
abstract class DisplayReadReceiptItem : EpoxyModelWithHolder<DisplayReadReceiptItem.Holder>() { abstract class DisplayReadReceiptItem : EpoxyModelWithHolder<DisplayReadReceiptItem.Holder>() {
@EpoxyAttribute lateinit var matrixItem: MatrixItem @EpoxyAttribute lateinit var matrixItem: MatrixItem
@EpoxyAttribute var timestamp: CharSequence? = null @EpoxyAttribute var timestamp: String? = null
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var userClicked: ClickListener? = null @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var userClicked: ClickListener? = null

View File

@ -26,6 +26,7 @@ import com.airbnb.epoxy.VisibilityState
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.date.DateFormatKind import im.vector.app.core.date.DateFormatKind
import im.vector.app.core.date.VectorDateFormatter import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.loadingItem
import im.vector.app.core.epoxy.noResultItem import im.vector.app.core.epoxy.noResultItem
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
@ -100,7 +101,7 @@ class SearchResultController @Inject constructor(
// Take new content first // Take new content first
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val text = ((event.content?.get("m.new_content") as? Content) ?: event.content)?.get("body") as? String ?: return@forEach val text = ((event.content?.get("m.new_content") as? Content) ?: event.content)?.get("body") as? String ?: return@forEach
val spannable = setHighLightedText(text, data.highlights) ?: return@forEach val spannable = setHighLightedText(text, data.highlights) ?: return@forEach
val eventDate = Calendar.getInstance().apply { val eventDate = Calendar.getInstance().apply {
@ -118,7 +119,7 @@ val text = ((event.content?.get("m.new_content") as? Content) ?: event.content)?
.id(eventAndSender.event.eventId) .id(eventAndSender.event.eventId)
.avatarRenderer(avatarRenderer) .avatarRenderer(avatarRenderer)
.formattedDate(dateFormatter.format(event.originServerTs, DateFormatKind.MESSAGE_SIMPLE)) .formattedDate(dateFormatter.format(event.originServerTs, DateFormatKind.MESSAGE_SIMPLE))
.spannable(spannable) .spannable(spannable.toEpoxyCharSequence())
.sender(eventAndSender.sender .sender(eventAndSender.sender
?: eventAndSender.event.senderId?.let { session.getRoomMember(it, data.roomId) }?.toMatrixItem()) ?: eventAndSender.event.senderId?.let { session.getRoomMember(it, data.roomId) }?.toMatrixItem())
.listener { listener?.onItemClicked(eventAndSender.event) } .listener { listener?.onItemClicked(eventAndSender.event) }

View File

@ -24,6 +24,7 @@ import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.charsequence.EpoxyCharSequence
import im.vector.app.core.epoxy.onClick import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.features.displayname.getBestName import im.vector.app.features.displayname.getBestName
@ -35,7 +36,7 @@ abstract class SearchResultItem : VectorEpoxyModel<SearchResultItem.Holder>() {
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute var formattedDate: String? = null @EpoxyAttribute var formattedDate: String? = null
@EpoxyAttribute lateinit var spannable: CharSequence @EpoxyAttribute lateinit var spannable: EpoxyCharSequence
@EpoxyAttribute var sender: MatrixItem? = null @EpoxyAttribute var sender: MatrixItem? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null
@ -46,7 +47,7 @@ abstract class SearchResultItem : VectorEpoxyModel<SearchResultItem.Holder>() {
sender?.let { avatarRenderer.render(it, holder.avatarImageView) } sender?.let { avatarRenderer.render(it, holder.avatarImageView) }
holder.memberNameView.setTextOrHide(sender?.getBestName()) holder.memberNameView.setTextOrHide(sender?.getBestName())
holder.timeView.text = formattedDate holder.timeView.text = formattedDate
holder.contentView.text = spannable holder.contentView.text = spannable.charSequence
} }
class Holder : VectorEpoxyHolder() { class Holder : VectorEpoxyHolder() {

View File

@ -27,6 +27,7 @@ import im.vector.app.core.epoxy.bottomsheet.bottomSheetActionItem
import im.vector.app.core.epoxy.bottomsheet.bottomSheetMessagePreviewItem import im.vector.app.core.epoxy.bottomsheet.bottomSheetMessagePreviewItem
import im.vector.app.core.epoxy.bottomsheet.bottomSheetQuickReactionsItem import im.vector.app.core.epoxy.bottomsheet.bottomSheetQuickReactionsItem
import im.vector.app.core.epoxy.bottomsheet.bottomSheetSendStateItem import im.vector.app.core.epoxy.bottomsheet.bottomSheetSendStateItem
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.DimensionConverter import im.vector.app.core.utils.DimensionConverter
@ -77,8 +78,8 @@ class MessageActionsEpoxyController @Inject constructor(
data(state.timelineEvent()?.buildImageContentRendererData(host.dimensionConverter.dpToPx(66))) data(state.timelineEvent()?.buildImageContentRendererData(host.dimensionConverter.dpToPx(66)))
userClicked { host.listener?.didSelectMenuAction(EventSharedAction.OpenUserProfile(state.informationData.senderId)) } userClicked { host.listener?.didSelectMenuAction(EventSharedAction.OpenUserProfile(state.informationData.senderId)) }
bindingOptions(bindingOptions) bindingOptions(bindingOptions)
body(body) body(body.toEpoxyCharSequence())
bodyDetails(host.eventDetailsFormatter.format(state.timelineEvent()?.root)) bodyDetails(host.eventDetailsFormatter.format(state.timelineEvent()?.root)?.toEpoxyCharSequence())
time(formattedDate) time(formattedDate)
} }

View File

@ -23,6 +23,7 @@ import com.airbnb.mvrx.Success
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.date.DateFormatKind import im.vector.app.core.date.DateFormatKind
import im.vector.app.core.date.VectorDateFormatter import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.core.ui.list.genericFooterItem
@ -75,7 +76,7 @@ class ViewEditHistoryEpoxyController @Inject constructor(
if (sourceEvents.isEmpty()) { if (sourceEvents.isEmpty()) {
genericItem { genericItem {
id("footer") id("footer")
title(host.stringProvider.getString(R.string.no_message_edits_found)) title(host.stringProvider.getString(R.string.no_message_edits_found).toEpoxyCharSequence())
} }
} else { } else {
var lastDate: Calendar? = null var lastDate: Calendar? = null
@ -133,8 +134,8 @@ class ViewEditHistoryEpoxyController @Inject constructor(
} }
genericItem { genericItem {
id(timelineEvent.eventId) id(timelineEvent.eventId)
title(host.dateFormatter.format(timelineEvent.originServerTs, DateFormatKind.EDIT_HISTORY_ROW)) title(host.dateFormatter.format(timelineEvent.originServerTs, DateFormatKind.EDIT_HISTORY_ROW).toEpoxyCharSequence())
description(spannedDiff ?: body) description((spannedDiff ?: body).toEpoxyCharSequence())
} }
} }
} }

View File

@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail.timeline.factory
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.DrawableProvider import im.vector.app.core.resources.DrawableProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
@ -110,7 +111,7 @@ class EncryptedItemFactory @Inject constructor(private val messageInformationDat
.leftGuideline(avatarSizeProvider.leftGuideline) .leftGuideline(avatarSizeProvider.leftGuideline)
.highlighted(params.isHighlighted) .highlighted(params.isHighlighted)
.attributes(attributes) .attributes(attributes)
.message(spannableStr) .message(spannableStr.toEpoxyCharSequence())
.movementMethod(createLinkMovementMethod(params.callback)) .movementMethod(createLinkMovementMethod(params.callback))
} }
else -> null else -> null

View File

@ -28,6 +28,7 @@ import dagger.Lazy
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.files.LocalFilesHelper import im.vector.app.core.files.LocalFilesHelper
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
@ -501,14 +502,14 @@ class MessageItemFactory @Inject constructor(
val bindingOptions = spanUtils.getBindingOptions(body) val bindingOptions = spanUtils.getBindingOptions(body)
val linkifiedBody = body.linkify(callback) val linkifiedBody = body.linkify(callback)
return MessageTextItem_().apply { return MessageTextItem_()
if (informationData.hasBeenEdited) { .message(
val spannable = annotateWithEdited(linkifiedBody, callback, informationData) if (informationData.hasBeenEdited) {
message(spannable) annotateWithEdited(linkifiedBody, callback, informationData)
} else { } else {
message(linkifiedBody) linkifiedBody
} }.toEpoxyCharSequence()
} )
.useBigFont(linkifiedBody.length <= MAX_NUMBER_OF_EMOJI_FOR_BIG_FONT * 2 && containsOnlyEmojis(linkifiedBody.toString())) .useBigFont(linkifiedBody.length <= MAX_NUMBER_OF_EMOJI_FOR_BIG_FONT * 2 && containsOnlyEmojis(linkifiedBody.toString()))
.bindingOptions(bindingOptions) .bindingOptions(bindingOptions)
.searchForPills(isFormatted) .searchForPills(isFormatted)
@ -530,13 +531,13 @@ class MessageItemFactory @Inject constructor(
.apply { .apply {
if (informationData.hasBeenEdited) { if (informationData.hasBeenEdited) {
val spannable = annotateWithEdited("", callback, informationData) val spannable = annotateWithEdited("", callback, informationData)
editedSpan(spannable) editedSpan(spannable.toEpoxyCharSequence())
} }
} }
.leftGuideline(avatarSizeProvider.leftGuideline) .leftGuideline(avatarSizeProvider.leftGuideline)
.attributes(attributes) .attributes(attributes)
.highlighted(highlight) .highlighted(highlight)
.message(formattedBody) .message(formattedBody.toEpoxyCharSequence())
} }
private fun annotateWithEdited(linkifiedBody: CharSequence, private fun annotateWithEdited(linkifiedBody: CharSequence,
@ -599,7 +600,7 @@ class MessageItemFactory @Inject constructor(
.imageContentRenderer(imageContentRenderer) .imageContentRenderer(imageContentRenderer)
.previewUrlCallback(callback) .previewUrlCallback(callback)
.attributes(attributes) .attributes(attributes)
.message(message) .message(message.toEpoxyCharSequence())
.bindingOptions(bindingOptions) .bindingOptions(bindingOptions)
.highlighted(highlight) .highlighted(highlight)
.movementMethod(createLinkMovementMethod(callback)) .movementMethod(createLinkMovementMethod(callback))
@ -617,14 +618,13 @@ class MessageItemFactory @Inject constructor(
val message = formattedBody.linkify(callback) val message = formattedBody.linkify(callback)
return MessageTextItem_() return MessageTextItem_()
.apply { .message(
if (informationData.hasBeenEdited) { if (informationData.hasBeenEdited) {
val spannable = annotateWithEdited(message, callback, informationData) annotateWithEdited(message, callback, informationData)
message(spannable) } else {
} else { message
message(message) }.toEpoxyCharSequence()
} )
}
.bindingOptions(bindingOptions) .bindingOptions(bindingOptions)
.leftGuideline(avatarSizeProvider.leftGuideline) .leftGuideline(avatarSizeProvider.leftGuideline)
.previewUrlRetriever(callback?.getPreviewUrlRetriever()) .previewUrlRetriever(callback?.getPreviewUrlRetriever())

View File

@ -16,6 +16,7 @@
package im.vector.app.features.home.room.detail.timeline.factory package im.vector.app.features.home.room.detail.timeline.factory
import im.vector.app.core.epoxy.charsequence.EpoxyCharSequence
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.timeline.format.NoticeEventFormatter import im.vector.app.features.home.room.detail.timeline.format.NoticeEventFormatter
import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
@ -37,7 +38,7 @@ class NoticeItemFactory @Inject constructor(private val eventFormatter: NoticeEv
val attributes = NoticeItem.Attributes( val attributes = NoticeItem.Attributes(
avatarRenderer = avatarRenderer, avatarRenderer = avatarRenderer,
informationData = informationData, informationData = informationData,
noticeText = formattedText, noticeText = EpoxyCharSequence(formattedText),
itemLongClickListener = { view -> itemLongClickListener = { view ->
params.callback?.onEventLongClicked(informationData, null, view) ?: false params.callback?.onEventLongClicked(informationData, null, view) ?: false
}, },

View File

@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail.timeline.factory
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.resources.UserPreferencesProvider import im.vector.app.core.resources.UserPreferencesProvider
import im.vector.app.features.home.room.detail.timeline.item.RoomCreateItem_ import im.vector.app.features.home.room.detail.timeline.item.RoomCreateItem_
@ -46,7 +47,7 @@ class RoomCreateItemFactory @Inject constructor(private val stringProvider: Stri
} }
} }
return RoomCreateItem_() return RoomCreateItem_()
.text(text) .text(text.toEpoxyCharSequence())
} }
private fun defaultRendering(params: TimelineItemFactoryParams): VectorEpoxyModel<*>? { private fun defaultRendering(params: TimelineItemFactoryParams): VectorEpoxyModel<*>? {

View File

@ -18,7 +18,5 @@ package im.vector.app.features.home.room.detail.timeline.item
data class BindingOptions( data class BindingOptions(
// Allowed by default // Allowed by default
val canUseTextFuture: Boolean = true, val canUseTextFuture: Boolean = true
// No need to prevent mutation by default
val preventMutation: Boolean = false
) )

View File

@ -26,7 +26,7 @@ import im.vector.app.core.epoxy.VectorEpoxyHolder
@EpoxyModelClass(layout = R.layout.item_timeline_event_day_separator) @EpoxyModelClass(layout = R.layout.item_timeline_event_day_separator)
abstract class DaySeparatorItem : EpoxyModelWithHolder<DaySeparatorItem.Holder>() { abstract class DaySeparatorItem : EpoxyModelWithHolder<DaySeparatorItem.Holder>() {
@EpoxyAttribute lateinit var formattedDay: CharSequence @EpoxyAttribute lateinit var formattedDay: String
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)

View File

@ -56,7 +56,7 @@ abstract class DefaultItem : BaseEventItem<DefaultItem.Holder>() {
data class Attributes( data class Attributes(
val avatarRenderer: AvatarRenderer, val avatarRenderer: AvatarRenderer,
val informationData: MessageInformationData, val informationData: MessageInformationData,
val text: CharSequence, val text: String,
val itemLongClickListener: View.OnLongClickListener? = null val itemLongClickListener: View.OnLongClickListener? = null
) )

View File

@ -20,6 +20,7 @@ import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.EpoxyCharSequence
import im.vector.app.core.epoxy.onClick import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.setTextOrHide
import me.saket.bettermovementmethod.BetterLinkMovementMethod import me.saket.bettermovementmethod.BetterLinkMovementMethod
@ -28,19 +29,19 @@ import me.saket.bettermovementmethod.BetterLinkMovementMethod
abstract class MessageBlockCodeItem : AbsMessageItem<MessageBlockCodeItem.Holder>() { abstract class MessageBlockCodeItem : AbsMessageItem<MessageBlockCodeItem.Holder>() {
@EpoxyAttribute @EpoxyAttribute
var message: CharSequence? = null var message: EpoxyCharSequence? = null
@EpoxyAttribute @EpoxyAttribute
var editedSpan: CharSequence? = null var editedSpan: EpoxyCharSequence? = null
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
holder.messageView.text = message holder.messageView.text = message?.charSequence
renderSendState(holder.messageView, holder.messageView) renderSendState(holder.messageView, holder.messageView)
holder.messageView.onClick(attributes.itemClickListener) holder.messageView.onClick(attributes.itemClickListener)
holder.messageView.setOnLongClickListener(attributes.itemLongClickListener) holder.messageView.setOnLongClickListener(attributes.itemLongClickListener)
holder.editedView.movementMethod = BetterLinkMovementMethod.getInstance() holder.editedView.movementMethod = BetterLinkMovementMethod.getInstance()
holder.editedView.setTextOrHide(editedSpan) holder.editedView.setTextOrHide(editedSpan?.charSequence)
} }
override fun getViewType() = STUB_ID override fun getViewType() = STUB_ID

View File

@ -34,7 +34,7 @@ import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStat
abstract class MessageFileItem : AbsMessageItem<MessageFileItem.Holder>() { abstract class MessageFileItem : AbsMessageItem<MessageFileItem.Holder>() {
@EpoxyAttribute @EpoxyAttribute
var filename: CharSequence = "" var filename: String = ""
@EpoxyAttribute @EpoxyAttribute
var mxcUrl: String = "" var mxcUrl: String = ""

View File

@ -24,9 +24,9 @@ import androidx.core.widget.TextViewCompat
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.EpoxyCharSequence
import im.vector.app.core.epoxy.onClick import im.vector.app.core.epoxy.onClick
import im.vector.app.core.epoxy.onLongClickIgnoringLinks import im.vector.app.core.epoxy.onLongClickIgnoringLinks
import im.vector.app.core.epoxy.util.preventMutation
import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.home.room.detail.timeline.TimelineEventController
import im.vector.app.features.home.room.detail.timeline.tools.findPillsAndProcess import im.vector.app.features.home.room.detail.timeline.tools.findPillsAndProcess
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
@ -42,7 +42,7 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
var searchForPills: Boolean = false var searchForPills: Boolean = false
@EpoxyAttribute @EpoxyAttribute
var message: CharSequence? = null var message: EpoxyCharSequence? = null
@EpoxyAttribute @EpoxyAttribute
var bindingOptions: BindingOptions? = null var bindingOptions: BindingOptions? = null
@ -82,14 +82,14 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
holder.messageView.textSize = 14F holder.messageView.textSize = 14F
} }
if (searchForPills) { if (searchForPills) {
message?.findPillsAndProcess(coroutineScope) { message?.charSequence?.findPillsAndProcess(coroutineScope) {
// mmm.. not sure this is so safe in regards to cell reuse // mmm.. not sure this is so safe in regards to cell reuse
it.bind(holder.messageView) it.bind(holder.messageView)
} }
} }
val textFuture = if (bindingOptions?.canUseTextFuture.orFalse()) { val textFuture = if (bindingOptions?.canUseTextFuture.orFalse()) {
PrecomputedTextCompat.getTextFuture( PrecomputedTextCompat.getTextFuture(
message ?: "", message?.charSequence ?: "",
TextViewCompat.getTextMetricsParams(holder.messageView), TextViewCompat.getTextMetricsParams(holder.messageView),
null) null)
} else { } else {
@ -104,11 +104,7 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
if (bindingOptions?.canUseTextFuture.orFalse()) { if (bindingOptions?.canUseTextFuture.orFalse()) {
holder.messageView.setTextFuture(textFuture) holder.messageView.setTextFuture(textFuture)
} else { } else {
holder.messageView.text = if (bindingOptions?.preventMutation.orFalse()) { holder.messageView.text = message?.charSequence
message.preventMutation()
} else {
message
}
} }
} }

View File

@ -23,6 +23,7 @@ import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.charsequence.EpoxyCharSequence
import im.vector.app.core.epoxy.onClick import im.vector.app.core.epoxy.onClick
import im.vector.app.core.ui.views.ShieldImageView import im.vector.app.core.ui.views.ShieldImageView
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
@ -37,7 +38,7 @@ abstract class NoticeItem : BaseEventItem<NoticeItem.Holder>() {
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
holder.noticeTextView.text = attributes.noticeText holder.noticeTextView.text = attributes.noticeText.charSequence
attributes.avatarRenderer.render(attributes.informationData.matrixItem, holder.avatarImageView) attributes.avatarRenderer.render(attributes.informationData.matrixItem, holder.avatarImageView)
holder.view.setOnLongClickListener(attributes.itemLongClickListener) holder.view.setOnLongClickListener(attributes.itemLongClickListener)
holder.avatarImageView.onClick(attributes.avatarClickListener) holder.avatarImageView.onClick(attributes.avatarClickListener)
@ -74,7 +75,7 @@ abstract class NoticeItem : BaseEventItem<NoticeItem.Holder>() {
data class Attributes( data class Attributes(
val avatarRenderer: AvatarRenderer, val avatarRenderer: AvatarRenderer,
val informationData: MessageInformationData, val informationData: MessageInformationData,
val noticeText: CharSequence, val noticeText: EpoxyCharSequence,
val itemLongClickListener: View.OnLongClickListener? = null, val itemLongClickListener: View.OnLongClickListener? = null,
val readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null, val readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null,
val avatarClickListener: ClickListener? = null val avatarClickListener: ClickListener? = null

View File

@ -22,17 +22,18 @@ import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.charsequence.EpoxyCharSequence
import me.saket.bettermovementmethod.BetterLinkMovementMethod import me.saket.bettermovementmethod.BetterLinkMovementMethod
@EpoxyModelClass(layout = R.layout.item_timeline_event_create) @EpoxyModelClass(layout = R.layout.item_timeline_event_create)
abstract class RoomCreateItem : VectorEpoxyModel<RoomCreateItem.Holder>() { abstract class RoomCreateItem : VectorEpoxyModel<RoomCreateItem.Holder>() {
@EpoxyAttribute lateinit var text: CharSequence @EpoxyAttribute lateinit var text: EpoxyCharSequence
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
holder.description.movementMethod = BetterLinkMovementMethod.getInstance() holder.description.movementMethod = BetterLinkMovementMethod.getInstance()
holder.description.text = text holder.description.text = text.charSequence
} }
class Holder : VectorEpoxyHolder() { class Holder : VectorEpoxyHolder() {

View File

@ -83,8 +83,8 @@ abstract class StatusTileTimelineItem : AbsBaseMessageItem<StatusTileTimelineIte
*/ */
data class Attributes( data class Attributes(
val shieldUIState: ShieldUIState, val shieldUIState: ShieldUIState,
val title: CharSequence, val title: String,
val description: CharSequence, val description: String,
override val informationData: MessageInformationData, override val informationData: MessageInformationData,
override val avatarRenderer: AvatarRenderer, override val avatarRenderer: AvatarRenderer,
override val messageColorProvider: MessageColorProvider, override val messageColorProvider: MessageColorProvider,

View File

@ -73,7 +73,7 @@ abstract class WidgetTileTimelineItem : AbsBaseMessageItem<WidgetTileTimelineIte
* This class holds all the common attributes for timeline items. * This class holds all the common attributes for timeline items.
*/ */
data class Attributes( data class Attributes(
val title: CharSequence, val title: String,
@DrawableRes @DrawableRes
val drawableStart: Int, val drawableStart: Int,
override val informationData: MessageInformationData, override val informationData: MessageInformationData,

View File

@ -24,8 +24,8 @@ import com.airbnb.epoxy.EpoxyModelWithHolder
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.charsequence.EpoxyCharSequence
import im.vector.app.core.epoxy.onClick import im.vector.app.core.epoxy.onClick
import im.vector.app.core.epoxy.util.preventMutation
/** /**
* Item displaying an emoji reaction (single line with emoji, author, time) * Item displaying an emoji reaction (single line with emoji, author, time)
@ -34,20 +34,20 @@ import im.vector.app.core.epoxy.util.preventMutation
abstract class ReactionInfoSimpleItem : EpoxyModelWithHolder<ReactionInfoSimpleItem.Holder>() { abstract class ReactionInfoSimpleItem : EpoxyModelWithHolder<ReactionInfoSimpleItem.Holder>() {
@EpoxyAttribute @EpoxyAttribute
lateinit var reactionKey: CharSequence lateinit var reactionKey: EpoxyCharSequence
@EpoxyAttribute @EpoxyAttribute
lateinit var authorDisplayName: CharSequence lateinit var authorDisplayName: String
@EpoxyAttribute @EpoxyAttribute
var timeStamp: CharSequence? = null var timeStamp: String? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var userClicked: ClickListener? = null var userClicked: ClickListener? = null
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
holder.emojiReactionView.text = reactionKey.preventMutation() holder.emojiReactionView.text = reactionKey.charSequence
holder.displayNameView.text = authorDisplayName holder.displayNameView.text = authorDisplayName
timeStamp?.let { timeStamp?.let {
holder.timeStampView.text = it holder.timeStampView.text = it

View File

@ -22,6 +22,7 @@ import com.airbnb.mvrx.Incomplete
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import im.vector.app.EmojiSpanify import im.vector.app.EmojiSpanify
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.core.ui.list.genericFooterItem
import im.vector.app.core.ui.list.genericLoaderItem import im.vector.app.core.ui.list.genericLoaderItem
@ -56,7 +57,7 @@ class ViewReactionsEpoxyController @Inject constructor(
reactionInfoSimpleItem { reactionInfoSimpleItem {
id(reactionInfo.eventId) id(reactionInfo.eventId)
timeStamp(reactionInfo.timestamp) timeStamp(reactionInfo.timestamp)
reactionKey(host.emojiSpanify.spanify(reactionInfo.reactionKey)) reactionKey(host.emojiSpanify.spanify(reactionInfo.reactionKey).toEpoxyCharSequence())
authorDisplayName(reactionInfo.authorName ?: reactionInfo.authorId) authorDisplayName(reactionInfo.authorName ?: reactionInfo.authorId)
userClicked { host.listener?.didSelectUser(reactionInfo.authorId) } userClicked { host.listener?.didSelectUser(reactionInfo.authorId) }
} }

View File

@ -32,7 +32,7 @@ import im.vector.app.features.themes.ThemeUtils
@EpoxyModelClass(layout = R.layout.item_room_category) @EpoxyModelClass(layout = R.layout.item_room_category)
abstract class RoomCategoryItem : VectorEpoxyModel<RoomCategoryItem.Holder>() { abstract class RoomCategoryItem : VectorEpoxyModel<RoomCategoryItem.Holder>() {
@EpoxyAttribute lateinit var title: CharSequence @EpoxyAttribute lateinit var title: String
@EpoxyAttribute var expanded: Boolean = false @EpoxyAttribute var expanded: Boolean = false
@EpoxyAttribute var unreadNotificationCount: Int = 0 @EpoxyAttribute var unreadNotificationCount: Int = 0
@EpoxyAttribute var showHighlighted: Boolean = false @EpoxyAttribute var showHighlighted: Boolean = false

View File

@ -39,7 +39,7 @@ abstract class RoomInvitationItem : VectorEpoxyModel<RoomInvitationItem.Holder>(
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute lateinit var matrixItem: MatrixItem @EpoxyAttribute lateinit var matrixItem: MatrixItem
@EpoxyAttribute var secondLine: CharSequence? = null @EpoxyAttribute var secondLine: String? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null
@EpoxyAttribute lateinit var changeMembershipState: ChangeMembershipState @EpoxyAttribute lateinit var changeMembershipState: ChangeMembershipState
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var acceptListener: ClickListener? = null @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var acceptListener: ClickListener? = null

View File

@ -30,6 +30,7 @@ import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.charsequence.EpoxyCharSequence
import im.vector.app.core.epoxy.onClick import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.ui.views.PresenceStateImageView import im.vector.app.core.ui.views.PresenceStateImageView
@ -44,16 +45,12 @@ import org.matrix.android.sdk.api.util.MatrixItem
@EpoxyModelClass(layout = R.layout.item_room) @EpoxyModelClass(layout = R.layout.item_room)
abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() { abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
@EpoxyAttribute lateinit var typingMessage: CharSequence @EpoxyAttribute lateinit var typingMessage: String
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute lateinit var matrixItem: MatrixItem @EpoxyAttribute lateinit var matrixItem: MatrixItem
// Used only for diff calculation @EpoxyAttribute lateinit var lastFormattedEvent: EpoxyCharSequence
@EpoxyAttribute lateinit var lastEvent: String @EpoxyAttribute lateinit var lastEventTime: String
// We use DoNotHash here as Spans are not implementing equals/hashcode
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) lateinit var lastFormattedEvent: CharSequence
@EpoxyAttribute lateinit var lastEventTime: CharSequence
@EpoxyAttribute var encryptionTrustLevel: RoomEncryptionTrustLevel? = null @EpoxyAttribute var encryptionTrustLevel: RoomEncryptionTrustLevel? = null
@EpoxyAttribute var userPresence: UserPresence? = null @EpoxyAttribute var userPresence: UserPresence? = null
@EpoxyAttribute var showPresence: Boolean = false @EpoxyAttribute var showPresence: Boolean = false
@ -76,7 +73,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
} }
holder.titleView.text = matrixItem.getBestName() holder.titleView.text = matrixItem.getBestName()
holder.lastEventTimeView.text = lastEventTime holder.lastEventTimeView.text = lastEventTime
holder.lastEventView.text = lastFormattedEvent holder.lastEventView.text = lastFormattedEvent.charSequence
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted)) holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted))
holder.unreadIndentIndicator.isVisible = hasUnreadMessage holder.unreadIndentIndicator.isVisible = hasUnreadMessage
holder.draftView.isVisible = hasDraft holder.draftView.isVisible = hasDraft

View File

@ -23,6 +23,7 @@ import im.vector.app.R
import im.vector.app.core.date.DateFormatKind import im.vector.app.core.date.DateFormatKind
import im.vector.app.core.date.VectorDateFormatter import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
@ -111,7 +112,7 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
val showHighlighted = roomSummary.highlightCount > 0 val showHighlighted = roomSummary.highlightCount > 0
val showSelected = selectedRoomIds.contains(roomSummary.roomId) val showSelected = selectedRoomIds.contains(roomSummary.roomId)
var latestFormattedEvent: CharSequence = "" var latestFormattedEvent: CharSequence = ""
var latestEventTime: CharSequence = "" var latestEventTime = ""
val latestEvent = roomSummary.latestPreviewableEvent val latestEvent = roomSummary.latestPreviewableEvent
if (latestEvent != null) { if (latestEvent != null) {
latestFormattedEvent = displayableEventFormatter.format(latestEvent, roomSummary.isDirect, roomSummary.isDirect.not()) latestFormattedEvent = displayableEventFormatter.format(latestEvent, roomSummary.isDirect, roomSummary.isDirect.not())
@ -129,8 +130,7 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
.matrixItem(roomSummary.toMatrixItem()) .matrixItem(roomSummary.toMatrixItem())
.lastEventTime(latestEventTime) .lastEventTime(latestEventTime)
.typingMessage(typingMessage) .typingMessage(typingMessage)
.lastEvent(latestFormattedEvent.toString()) .lastFormattedEvent(latestFormattedEvent.toEpoxyCharSequence())
.lastFormattedEvent(latestFormattedEvent)
.showHighlighted(showHighlighted) .showHighlighted(showHighlighted)
.showSelected(showSelected) .showSelected(showSelected)
.hasFailedSending(roomSummary.hasFailedSending) .hasFailedSending(roomSummary.hasFailedSending)

View File

@ -35,8 +35,7 @@ class SpanUtils @Inject constructor(
} }
return BindingOptions( return BindingOptions(
canUseTextFuture = canUseTextFuture(emojiCharSequence), canUseTextFuture = canUseTextFuture(emojiCharSequence)
preventMutation = mustPreventMutation(emojiCharSequence)
) )
} }
@ -49,11 +48,4 @@ class SpanUtils @Inject constructor(
.getSpans(0, spanned.length, Any::class.java) .getSpans(0, spanned.length, Any::class.java)
.all { it !is StrikethroughSpan && it !is UnderlineSpan && it !is MetricAffectingSpan } .all { it !is StrikethroughSpan && it !is UnderlineSpan && it !is MetricAffectingSpan }
} }
// Workaround for setting text during binding which mutate the text itself
private fun mustPreventMutation(spanned: Spanned): Boolean {
return spanned
.getSpans(0, spanned.length, Any::class.java)
.any { it is MetricAffectingSpan }
}
} }

View File

@ -20,6 +20,7 @@ import android.view.Gravity
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import com.airbnb.epoxy.EpoxyController import com.airbnb.epoxy.EpoxyController
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.list.ItemStyle import im.vector.app.core.ui.list.ItemStyle
@ -49,7 +50,7 @@ class CreatePollController @Inject constructor(
genericItem { genericItem {
id("question_title") id("question_title")
style(ItemStyle.BIG_TEXT) style(ItemStyle.BIG_TEXT)
title(host.stringProvider.getString(R.string.create_poll_question_title)) title(host.stringProvider.getString(R.string.create_poll_question_title).toEpoxyCharSequence())
} }
val questionImeAction = if (currentState.options.isEmpty()) EditorInfo.IME_ACTION_DONE else EditorInfo.IME_ACTION_NEXT val questionImeAction = if (currentState.options.isEmpty()) EditorInfo.IME_ACTION_DONE else EditorInfo.IME_ACTION_NEXT
@ -69,7 +70,7 @@ class CreatePollController @Inject constructor(
genericItem { genericItem {
id("options_title") id("options_title")
style(ItemStyle.BIG_TEXT) style(ItemStyle.BIG_TEXT)
title(host.stringProvider.getString(R.string.create_poll_options_title)) title(host.stringProvider.getString(R.string.create_poll_options_title).toEpoxyCharSequence())
} }
currentState.options.forEachIndexed { index, option -> currentState.options.forEachIndexed { index, option ->

View File

@ -20,6 +20,7 @@ import com.airbnb.epoxy.TypedEpoxyController
import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Loading
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.epoxy.profiles.buildProfileAction import im.vector.app.core.epoxy.profiles.buildProfileAction
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.list.genericPillItem import im.vector.app.core.ui.list.genericPillItem
@ -52,7 +53,7 @@ class CreateSubSpaceController @Inject constructor(
id("beta") id("beta")
imageRes(R.drawable.ic_beta_pill) imageRes(R.drawable.ic_beta_pill)
tintIcon(false) tintIcon(false)
text(host.stringProvider.getString(R.string.space_add_space_to_any_space_you_manage)) text(host.stringProvider.getString(R.string.space_add_space_to_any_space_you_manage).toEpoxyCharSequence())
} }
formEditableSquareAvatarItem { formEditableSquareAvatarItem {

View File

@ -22,6 +22,7 @@ import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.Uninitialized
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.epoxy.errorWithRetryItem import im.vector.app.core.epoxy.errorWithRetryItem
import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.loadingItem
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
@ -40,7 +41,7 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider:
private val colorProvider: ColorProvider, private val colorProvider: ColorProvider,
private val dimensionConverter: DimensionConverter, private val dimensionConverter: DimensionConverter,
private val vectorPreferences: VectorPreferences) : private val vectorPreferences: VectorPreferences) :
TypedEpoxyController<DeviceListViewState>() { TypedEpoxyController<DeviceListViewState>() {
interface InteractionListener { interface InteractionListener {
fun onDeviceSelected(device: CryptoDeviceInfo) fun onDeviceSelected(device: CryptoDeviceInfo)
@ -75,11 +76,11 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider:
style(ItemStyle.BIG_TEXT) style(ItemStyle.BIG_TEXT)
titleIconResourceId(if (allGreen) R.drawable.ic_shield_trusted else R.drawable.ic_shield_warning) titleIconResourceId(if (allGreen) R.drawable.ic_shield_trusted else R.drawable.ic_shield_warning)
title( title(
host.stringProvider.getString( host.stringProvider
if (allGreen) R.string.verification_profile_verified else R.string.verification_profile_warning .getString(if (allGreen) R.string.verification_profile_verified else R.string.verification_profile_warning)
) .toEpoxyCharSequence()
) )
description(host.stringProvider.getString(R.string.verification_conclusion_ok_notice)) description(host.stringProvider.getString(R.string.verification_conclusion_ok_notice).toEpoxyCharSequence())
} }
if (vectorPreferences.developerMode()) { if (vectorPreferences.developerMode()) {
@ -90,7 +91,7 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider:
genericItem { genericItem {
id("sessions") id("sessions")
style(ItemStyle.BIG_TEXT) style(ItemStyle.BIG_TEXT)
title(host.stringProvider.getString(R.string.room_member_profile_sessions_section_title)) title(host.stringProvider.getString(R.string.room_member_profile_sessions_section_title).toEpoxyCharSequence())
} }
if (deviceList.isEmpty()) { if (deviceList.isEmpty()) {
// Can this really happen? // Can this really happen?
@ -105,7 +106,7 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider:
id(device.deviceId) id(device.deviceId)
titleIconResourceId(if (device.isVerified) R.drawable.ic_shield_trusted else R.drawable.ic_shield_warning) titleIconResourceId(if (device.isVerified) R.drawable.ic_shield_trusted else R.drawable.ic_shield_warning)
apply { apply {
if (host.vectorPreferences.developerMode()) { val title = if (host.vectorPreferences.developerMode()) {
val seq = span { val seq = span {
+(device.displayName() ?: device.deviceId) +(device.displayName() ?: device.deviceId)
+"\n" +"\n"
@ -115,10 +116,11 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider:
textSize = host.dimensionConverter.spToPx(14) textSize = host.dimensionConverter.spToPx(14)
} }
} }
title(seq) seq
} else { } else {
title(device.displayName() ?: device.deviceId) device.displayName() ?: device.deviceId
} }
title(title.toEpoxyCharSequence())
} }
value( value(
host.stringProvider.getString( host.stringProvider.getString(
@ -163,7 +165,7 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider:
textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary) textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
textSize = host.dimensionConverter.spToPx(12) textSize = host.dimensionConverter.spToPx(12)
} }
} }.toEpoxyCharSequence()
) )
} }
} }
@ -179,7 +181,7 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider:
textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary) textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
textSize = host.dimensionConverter.spToPx(12) textSize = host.dimensionConverter.spToPx(12)
} }
} }.toEpoxyCharSequence()
) )
} }
} }
@ -195,7 +197,7 @@ class DeviceListEpoxyController @Inject constructor(private val stringProvider:
textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary) textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
textSize = host.dimensionConverter.spToPx(12) textSize = host.dimensionConverter.spToPx(12)
} }
} }.toEpoxyCharSequence()
) )
} }
} }

View File

@ -18,6 +18,7 @@ package im.vector.app.features.roommemberprofile.devices
import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.epoxy.TypedEpoxyController
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.list.ItemStyle import im.vector.app.core.ui.list.ItemStyle
@ -52,9 +53,9 @@ class DeviceTrustInfoEpoxyController @Inject constructor(private val stringProvi
style(ItemStyle.BIG_TEXT) style(ItemStyle.BIG_TEXT)
titleIconResourceId(if (isVerified) R.drawable.ic_shield_trusted else R.drawable.ic_shield_warning) titleIconResourceId(if (isVerified) R.drawable.ic_shield_trusted else R.drawable.ic_shield_warning)
title( title(
host.stringProvider.getString( host.stringProvider
if (isVerified) R.string.verification_profile_verified else R.string.verification_profile_warning .getString(if (isVerified) R.string.verification_profile_verified else R.string.verification_profile_warning)
) .toEpoxyCharSequence()
) )
} }
genericFooterItem { genericFooterItem {
@ -88,7 +89,7 @@ class DeviceTrustInfoEpoxyController @Inject constructor(private val stringProvi
textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary) textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
textSize = host.dimensionConverter.spToPx(14) textSize = host.dimensionConverter.spToPx(14)
} }
} }.toEpoxyCharSequence()
) )
} }

View File

@ -21,7 +21,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
class RoomJoinRuleRadioAction( class RoomJoinRuleRadioAction(
val roomJoinRule: RoomJoinRules, val roomJoinRule: RoomJoinRules,
title: CharSequence, title: String,
description: String, description: String,
isSelected: Boolean isSelected: Boolean
) : BottomSheetGenericRadioAction( ) : BottomSheetGenericRadioAction(

View File

@ -17,6 +17,7 @@ package im.vector.app.features.settings.crosssigning
import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.epoxy.TypedEpoxyController
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.list.genericButtonItem import im.vector.app.core.ui.list.genericButtonItem
@ -47,7 +48,7 @@ class CrossSigningSettingsController @Inject constructor(
genericItem { genericItem {
id("can") id("can")
titleIconResourceId(R.drawable.ic_shield_trusted) titleIconResourceId(R.drawable.ic_shield_trusted)
title(host.stringProvider.getString(R.string.encryption_information_dg_xsigning_complete)) title(host.stringProvider.getString(R.string.encryption_information_dg_xsigning_complete).toEpoxyCharSequence())
} }
genericButtonItem { genericButtonItem {
id("Reset") id("Reset")
@ -61,7 +62,7 @@ class CrossSigningSettingsController @Inject constructor(
genericItem { genericItem {
id("trusted") id("trusted")
titleIconResourceId(R.drawable.ic_shield_custom) titleIconResourceId(R.drawable.ic_shield_custom)
title(host.stringProvider.getString(R.string.encryption_information_dg_xsigning_trusted)) title(host.stringProvider.getString(R.string.encryption_information_dg_xsigning_trusted).toEpoxyCharSequence())
} }
genericButtonItem { genericButtonItem {
id("Reset") id("Reset")
@ -75,7 +76,7 @@ class CrossSigningSettingsController @Inject constructor(
genericItem { genericItem {
id("enable") id("enable")
titleIconResourceId(R.drawable.ic_shield_black) titleIconResourceId(R.drawable.ic_shield_black)
title(host.stringProvider.getString(R.string.encryption_information_dg_xsigning_not_trusted)) title(host.stringProvider.getString(R.string.encryption_information_dg_xsigning_not_trusted).toEpoxyCharSequence())
} }
genericButtonItem { genericButtonItem {
id("Reset") id("Reset")
@ -88,7 +89,7 @@ class CrossSigningSettingsController @Inject constructor(
else -> { else -> {
genericItem { genericItem {
id("not") id("not")
title(host.stringProvider.getString(R.string.encryption_information_dg_xsigning_disabled)) title(host.stringProvider.getString(R.string.encryption_information_dg_xsigning_disabled).toEpoxyCharSequence())
} }
genericPositiveButtonItem { genericPositiveButtonItem {
@ -115,7 +116,7 @@ class CrossSigningSettingsController @Inject constructor(
textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary) textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
textSize = host.dimensionConverter.spToPx(12) textSize = host.dimensionConverter.spToPx(12)
} }
} }.toEpoxyCharSequence()
) )
} }
} }
@ -131,7 +132,7 @@ class CrossSigningSettingsController @Inject constructor(
textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary) textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
textSize = host.dimensionConverter.spToPx(12) textSize = host.dimensionConverter.spToPx(12)
} }
} }.toEpoxyCharSequence()
) )
} }
} }
@ -147,7 +148,7 @@ class CrossSigningSettingsController @Inject constructor(
textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary) textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
textSize = host.dimensionConverter.spToPx(12) textSize = host.dimensionConverter.spToPx(12)
} }
} }.toEpoxyCharSequence()
) )
} }
} }

View File

@ -18,6 +18,7 @@ package im.vector.app.features.settings.devices
import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.epoxy.TypedEpoxyController
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.loadingItem
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
@ -34,7 +35,7 @@ import javax.inject.Inject
class DeviceVerificationInfoBottomSheetController @Inject constructor( class DeviceVerificationInfoBottomSheetController @Inject constructor(
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val colorProvider: ColorProvider) : private val colorProvider: ColorProvider) :
TypedEpoxyController<DeviceVerificationInfoBottomSheetViewState>() { TypedEpoxyController<DeviceVerificationInfoBottomSheetViewState>() {
var callback: Callback? = null var callback: Callback? = null
@ -88,8 +89,8 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
id("trust${cryptoDeviceInfo.deviceId}") id("trust${cryptoDeviceInfo.deviceId}")
style(ItemStyle.BIG_TEXT) style(ItemStyle.BIG_TEXT)
titleIconResourceId(shield) titleIconResourceId(shield)
title(host.stringProvider.getString(R.string.encryption_information_verified)) title(host.stringProvider.getString(R.string.encryption_information_verified).toEpoxyCharSequence())
description(host.stringProvider.getString(R.string.settings_active_sessions_verified_device_desc)) description(host.stringProvider.getString(R.string.settings_active_sessions_verified_device_desc).toEpoxyCharSequence())
} }
} else if (data.canVerifySession) { } else if (data.canVerifySession) {
// You need to complete security, only if there are other session(s) available, or if 4S contains secrets // You need to complete security, only if there are other session(s) available, or if 4S contains secrets
@ -97,12 +98,11 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
id("trust${cryptoDeviceInfo.deviceId}") id("trust${cryptoDeviceInfo.deviceId}")
style(ItemStyle.BIG_TEXT) style(ItemStyle.BIG_TEXT)
titleIconResourceId(shield) titleIconResourceId(shield)
title(host.stringProvider.getString(R.string.crosssigning_verify_this_session)) title(host.stringProvider.getString(R.string.crosssigning_verify_this_session).toEpoxyCharSequence())
if (data.hasOtherSessions) { description(host.stringProvider
description(host.stringProvider.getString(R.string.confirm_your_identity)) .getString(if (data.hasOtherSessions) R.string.confirm_your_identity else R.string.confirm_your_identity_quad_s)
} else { .toEpoxyCharSequence()
description(host.stringProvider.getString(R.string.confirm_your_identity_quad_s)) )
}
} }
} }
} else { } else {
@ -117,16 +117,16 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
id("trust${cryptoDeviceInfo.deviceId}") id("trust${cryptoDeviceInfo.deviceId}")
style(ItemStyle.BIG_TEXT) style(ItemStyle.BIG_TEXT)
titleIconResourceId(shield) titleIconResourceId(shield)
title(host.stringProvider.getString(R.string.encryption_information_verified)) title(host.stringProvider.getString(R.string.encryption_information_verified).toEpoxyCharSequence())
description(host.stringProvider.getString(R.string.settings_active_sessions_verified_device_desc)) description(host.stringProvider.getString(R.string.settings_active_sessions_verified_device_desc).toEpoxyCharSequence())
} }
} else { } else {
genericItem { genericItem {
id("trust${cryptoDeviceInfo.deviceId}") id("trust${cryptoDeviceInfo.deviceId}")
titleIconResourceId(shield) titleIconResourceId(shield)
style(ItemStyle.BIG_TEXT) style(ItemStyle.BIG_TEXT)
title(host.stringProvider.getString(R.string.encryption_information_not_verified)) title(host.stringProvider.getString(R.string.encryption_information_not_verified).toEpoxyCharSequence())
description(host.stringProvider.getString(R.string.settings_active_sessions_unverified_device_desc)) description(host.stringProvider.getString(R.string.settings_active_sessions_unverified_device_desc).toEpoxyCharSequence())
} }
} }
} }
@ -135,8 +135,8 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
// DEVICE INFO SECTION // DEVICE INFO SECTION
genericItem { genericItem {
id("info${cryptoDeviceInfo.deviceId}") id("info${cryptoDeviceInfo.deviceId}")
title(cryptoDeviceInfo.displayName() ?: "") title(cryptoDeviceInfo.displayName().orEmpty().toEpoxyCharSequence())
description("(${cryptoDeviceInfo.deviceId})") description("(${cryptoDeviceInfo.deviceId})".toEpoxyCharSequence())
} }
if (isMine && !currentSessionIsTrusted && data.canVerifySession) { if (isMine && !currentSessionIsTrusted && data.canVerifySession) {
@ -176,24 +176,24 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
id("trust${cryptoDeviceInfo.deviceId}") id("trust${cryptoDeviceInfo.deviceId}")
style(ItemStyle.BIG_TEXT) style(ItemStyle.BIG_TEXT)
titleIconResourceId(shield) titleIconResourceId(shield)
title(host.stringProvider.getString(R.string.encryption_information_verified)) title(host.stringProvider.getString(R.string.encryption_information_verified).toEpoxyCharSequence())
description(host.stringProvider.getString(R.string.settings_active_sessions_verified_device_desc)) description(host.stringProvider.getString(R.string.settings_active_sessions_verified_device_desc).toEpoxyCharSequence())
} }
} else { } else {
genericItem { genericItem {
id("trust${cryptoDeviceInfo.deviceId}") id("trust${cryptoDeviceInfo.deviceId}")
titleIconResourceId(shield) titleIconResourceId(shield)
style(ItemStyle.BIG_TEXT) style(ItemStyle.BIG_TEXT)
title(host.stringProvider.getString(R.string.encryption_information_not_verified)) title(host.stringProvider.getString(R.string.encryption_information_not_verified).toEpoxyCharSequence())
description(host.stringProvider.getString(R.string.settings_active_sessions_unverified_device_desc)) description(host.stringProvider.getString(R.string.settings_active_sessions_unverified_device_desc).toEpoxyCharSequence())
} }
} }
// DEVICE INFO SECTION // DEVICE INFO SECTION
genericItem { genericItem {
id("info${cryptoDeviceInfo.deviceId}") id("info${cryptoDeviceInfo.deviceId}")
title(cryptoDeviceInfo.displayName() ?: "") title(cryptoDeviceInfo.displayName().orEmpty().toEpoxyCharSequence())
description("(${cryptoDeviceInfo.deviceId})") description("(${cryptoDeviceInfo.deviceId})".toEpoxyCharSequence())
} }
// ACTIONS // ACTIONS
@ -287,8 +287,8 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
val info = data.deviceInfo.invoke() ?: return val info = data.deviceInfo.invoke() ?: return
genericItem { genericItem {
id("info${info.deviceId}") id("info${info.deviceId}")
title(info.displayName ?: "") title(info.displayName.orEmpty().toEpoxyCharSequence())
description("(${info.deviceId})") description("(${info.deviceId})".toEpoxyCharSequence())
} }
genericFooterItem { genericFooterItem {

View File

@ -22,6 +22,7 @@ import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.loadingItem
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.core.ui.list.genericFooterItem
@ -67,7 +68,7 @@ class AccountDataEpoxyController @Inject constructor(
dataList.forEach { accountData -> dataList.forEach { accountData ->
genericWithValueItem { genericWithValueItem {
id(accountData.type) id(accountData.type)
title(accountData.type) title(accountData.type.toEpoxyCharSequence())
itemClickAction { itemClickAction {
host.interactionListener?.didTap(accountData) host.interactionListener?.didTap(accountData)
} }

View File

@ -21,8 +21,8 @@ import com.airbnb.epoxy.paging.PagedListEpoxyController
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.date.DateFormatKind import im.vector.app.core.date.DateFormatKind
import im.vector.app.core.date.VectorDateFormatter import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.list.GenericItem_ import im.vector.app.core.ui.list.GenericItem_
import im.vector.app.core.utils.createUIHandler import im.vector.app.core.utils.createUIHandler
import me.gujun.android.span.span import me.gujun.android.span.span
@ -38,7 +38,6 @@ import org.matrix.android.sdk.internal.crypto.model.rest.SecretShareRequest
import javax.inject.Inject import javax.inject.Inject
class GossipingTrailPagedEpoxyController @Inject constructor( class GossipingTrailPagedEpoxyController @Inject constructor(
private val stringProvider: StringProvider,
private val vectorDateFormatter: VectorDateFormatter, private val vectorDateFormatter: VectorDateFormatter,
private val colorProvider: ColorProvider private val colorProvider: ColorProvider
) : PagedListEpoxyController<Event>( ) : PagedListEpoxyController<Event>(
@ -63,7 +62,7 @@ class GossipingTrailPagedEpoxyController @Inject constructor(
"${event.getClearType()} [encrypted]" "${event.getClearType()} [encrypted]"
} else { } else {
event.type event.type
} }?.toEpoxyCharSequence()
) )
description( description(
span { span {
@ -157,11 +156,11 @@ class GossipingTrailPagedEpoxyController @Inject constructor(
+"${content?.requestingDeviceId}" +"${content?.requestingDeviceId}"
} else if (event.getClearType() == EventType.ENCRYPTED) { } else if (event.getClearType() == EventType.ENCRYPTED) {
span("**Failed to Decrypt** ${event.mCryptoError}") { span("**Failed to Decrypt** ${event.mCryptoError}") {
textColor = host.colorProvider.getColorFromAttribute(R.attr.colorError) textColor = host.colorProvider.getColorFromAttribute(R.attr.colorError)
} }
} }
} }
} }.toEpoxyCharSequence()
) )
} }
} }

View File

@ -20,6 +20,7 @@ import com.airbnb.epoxy.EpoxyModel
import com.airbnb.epoxy.paging.PagedListEpoxyController import com.airbnb.epoxy.paging.PagedListEpoxyController
import im.vector.app.core.date.DateFormatKind import im.vector.app.core.date.DateFormatKind
import im.vector.app.core.date.VectorDateFormatter import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.ui.list.GenericItem_ import im.vector.app.core.ui.list.GenericItem_
import im.vector.app.core.utils.createUIHandler import im.vector.app.core.utils.createUIHandler
import me.gujun.android.span.span import me.gujun.android.span.span
@ -45,7 +46,7 @@ class IncomingKeyRequestPagedController @Inject constructor(
return GenericItem_().apply { return GenericItem_().apply {
id(roomKeyRequest.requestId) id(roomKeyRequest.requestId)
title(roomKeyRequest.requestId) title(roomKeyRequest.requestId?.toEpoxyCharSequence())
description( description(
span { span {
span("From: ") { span("From: ") {
@ -65,7 +66,7 @@ class IncomingKeyRequestPagedController @Inject constructor(
textStyle = "bold" textStyle = "bold"
} }
+roomKeyRequest.state.name +roomKeyRequest.state.name
} }.toEpoxyCharSequence()
) )
} }
} }

View File

@ -18,6 +18,7 @@ package im.vector.app.features.settings.devtools
import com.airbnb.epoxy.EpoxyModel import com.airbnb.epoxy.EpoxyModel
import com.airbnb.epoxy.paging.PagedListEpoxyController import com.airbnb.epoxy.paging.PagedListEpoxyController
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.ui.list.GenericItem_ import im.vector.app.core.ui.list.GenericItem_
import im.vector.app.core.utils.createUIHandler import im.vector.app.core.utils.createUIHandler
import me.gujun.android.span.span import me.gujun.android.span.span
@ -40,7 +41,7 @@ class OutgoingKeyRequestPagedController @Inject constructor() : PagedListEpoxyCo
return GenericItem_().apply { return GenericItem_().apply {
id(roomKeyRequest.requestId) id(roomKeyRequest.requestId)
title(roomKeyRequest.requestId) title(roomKeyRequest.requestId.toEpoxyCharSequence())
description( description(
span { span {
span("roomId: ") { span("roomId: ") {
@ -56,7 +57,7 @@ class OutgoingKeyRequestPagedController @Inject constructor() : PagedListEpoxyCo
textStyle = "bold" textStyle = "bold"
} }
+roomKeyRequest.state.name +roomKeyRequest.state.name
} }.toEpoxyCharSequence()
) )
} }
} }

View File

@ -22,6 +22,7 @@ import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.Uninitialized
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.epoxy.errorWithRetryItem import im.vector.app.core.epoxy.errorWithRetryItem
import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.loadingItem
import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.error.ErrorFormatter
@ -143,14 +144,14 @@ class HomeserverSettingsController @Inject constructor(
genericWithValueItem { genericWithValueItem {
id("room_version_default") id("room_version_default")
title(host.stringProvider.getString(R.string.settings_server_default_room_version)) title(host.stringProvider.getString(R.string.settings_server_default_room_version).toEpoxyCharSequence())
value(roomCapabilities.defaultRoomVersion) value(roomCapabilities.defaultRoomVersion)
} }
roomCapabilities.supportedVersion.forEach { roomCapabilities.supportedVersion.forEach {
genericWithValueItem { genericWithValueItem {
id("room_version_${it.version}") id("room_version_${it.version}")
title(it.version) title(it.version.toEpoxyCharSequence())
value( value(
host.stringProvider.getString( host.stringProvider.getString(
when (it.status) { when (it.status) {

View File

@ -20,6 +20,7 @@ import android.text.InputType
import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.epoxy.TypedEpoxyController
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.list.ItemStyle import im.vector.app.core.ui.list.ItemStyle
@ -57,7 +58,7 @@ class SpaceAdd3pidEpoxyController @Inject constructor(
genericPillItem { genericPillItem {
id("no_IDS") id("no_IDS")
imageRes(R.drawable.ic_baseline_perm_contact_calendar_24) imageRes(R.drawable.ic_baseline_perm_contact_calendar_24)
text(host.stringProvider.getString(R.string.create_space_identity_server_info_none)) text(host.stringProvider.getString(R.string.create_space_identity_server_info_none).toEpoxyCharSequence())
} }
genericButtonItem { genericButtonItem {
id("Discover_Settings") id("Discover_Settings")

View File

@ -24,6 +24,7 @@ import com.airbnb.mvrx.Incomplete
import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.Uninitialized
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.epoxy.errorWithRetryItem import im.vector.app.core.epoxy.errorWithRetryItem
import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.loadingItem
import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.error.ErrorFormatter
@ -87,7 +88,7 @@ class SpaceDirectoryController @Inject constructor(
span(host.stringProvider.getString(R.string.spaces_no_server_support_description)) { span(host.stringProvider.getString(R.string.spaces_no_server_support_description)) {
textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary) textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
} }
} }.toEpoxyCharSequence()
) )
} }
} else { } else {

View File

@ -20,6 +20,7 @@ import androidx.recyclerview.widget.DiffUtil
import com.airbnb.epoxy.EpoxyModel import com.airbnb.epoxy.EpoxyModel
import com.airbnb.epoxy.paging.PagedListEpoxyController import com.airbnb.epoxy.paging.PagedListEpoxyController
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.ui.list.GenericPillItem_ import im.vector.app.core.ui.list.GenericPillItem_
import im.vector.app.core.utils.createUIHandler import im.vector.app.core.utils.createUIHandler
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
@ -56,7 +57,7 @@ class AddRoomListController @Inject constructor(
var listener: Listener? = null var listener: Listener? = null
var ignoreRooms: List<String>? = null var ignoreRooms: List<String>? = null
var subHeaderText: CharSequence? = null var subHeaderText: String? = null
var initialLoadOccurred = false var initialLoadOccurred = false
@ -130,7 +131,7 @@ class AddRoomListController @Inject constructor(
add( add(
GenericPillItem_().apply { GenericPillItem_().apply {
id("sub_header") id("sub_header")
text(host.subHeaderText) text(host.subHeaderText?.toEpoxyCharSequence())
imageRes(R.drawable.ic_info) imageRes(R.drawable.ic_info)
} }
) )

View File

@ -18,6 +18,7 @@ package im.vector.app.features.spaces.people
import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.epoxy.TypedEpoxyController
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.epoxy.dividerItem import im.vector.app.core.epoxy.dividerItem
import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.loadingItem
import im.vector.app.core.epoxy.profiles.profileMatrixItemWithPowerLevel import im.vector.app.core.epoxy.profiles.profileMatrixItemWithPowerLevel
@ -128,7 +129,7 @@ class SpacePeopleListController @Inject constructor(
span { span {
+"\n" +"\n"
+host.stringProvider.getString(R.string.no_result_placeholder) +host.stringProvider.getString(R.string.no_result_placeholder)
} }.toEpoxyCharSequence()
) )
description( description(
span { span {
@ -138,7 +139,7 @@ class SpacePeopleListController @Inject constructor(
textColor = host.colorProvider.getColorFromAttribute(R.attr.colorPrimary) textColor = host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)
textStyle = "bold" textStyle = "bold"
} }
} }.toEpoxyCharSequence()
) )
itemClickAction { itemClickAction {
host.listener?.onInviteToSpaceSelected() host.listener?.onInviteToSpaceSelected()

View File

@ -31,7 +31,7 @@ import im.vector.app.core.extensions.setTextOrHide
@EpoxyModelClass(layout = R.layout.item_contact_action) @EpoxyModelClass(layout = R.layout.item_contact_action)
abstract class ActionItem : VectorEpoxyModel<ActionItem.Holder>() { abstract class ActionItem : VectorEpoxyModel<ActionItem.Holder>() {
@EpoxyAttribute var title: CharSequence? = null @EpoxyAttribute var title: String? = null
@EpoxyAttribute @DrawableRes var actionIconRes: Int? = null @EpoxyAttribute @DrawableRes var actionIconRes: Int? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var clickAction: ClickListener? = null @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var clickAction: ClickListener? = null

View File

@ -22,6 +22,7 @@ import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.Uninitialized
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.charsequence.toEpoxyCharSequence
import im.vector.app.core.epoxy.errorWithRetryItem import im.vector.app.core.epoxy.errorWithRetryItem
import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.loadingItem
import im.vector.app.core.epoxy.noResultItem import im.vector.app.core.epoxy.noResultItem
@ -154,7 +155,7 @@ class UserListController @Inject constructor(private val session: Session,
textStyle = "bold" textStyle = "bold"
textColor = host.colorProvider.getColorFromAttribute(R.attr.colorPrimary) textColor = host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)
} }
} }.toEpoxyCharSequence()
) )
itemClickAction { itemClickAction {
host.callback?.giveIdentityServerConsent() host.callback?.giveIdentityServerConsent()
@ -182,7 +183,7 @@ class UserListController @Inject constructor(private val session: Session,
textStyle = "bold" textStyle = "bold"
textColor = host.colorProvider.getColorFromAttribute(R.attr.colorPrimary) textColor = host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)
} }
} }.toEpoxyCharSequence()
) )
itemClickAction { itemClickAction {
host.callback?.onSetupDiscovery() host.callback?.onSetupDiscovery()