Unverified Commit 309d3712 authored by Neil Alexander's avatar Neil Alexander
Browse files

Merge tag 'v1.1.7' into neilalexander/p2p

parents 6eaf1256 a9c966b8
......@@ -161,7 +161,7 @@ Formatter\.formatShortFileSize===1
# android\.text\.TextUtils
### This is not a rule, but a warning: the number of "enum class" has changed. For Json classes, it is mandatory that they have `@JsonClass(generateAdapter = false)`. If the enum is not used as a Json class, change the value in file forbidden_strings_in_code.txt
enum class===99
enum class===100
### Do not import temporary legacy classes
import org.matrix.android.sdk.internal.legacy.riot===3
......
......@@ -126,6 +126,8 @@ import im.vector.app.features.spaces.create.CreateSpaceDefaultRoomsFragment
import im.vector.app.features.spaces.create.CreateSpaceDetailsFragment
import im.vector.app.features.spaces.explore.SpaceDirectoryFragment
import im.vector.app.features.spaces.manage.SpaceAddRoomFragment
import im.vector.app.features.spaces.manage.SpaceManageRoomsFragment
import im.vector.app.features.spaces.manage.SpaceSettingsFragment
import im.vector.app.features.spaces.people.SpacePeopleFragment
import im.vector.app.features.spaces.preview.SpacePreviewFragment
import im.vector.app.features.terms.ReviewTermsFragment
......@@ -690,4 +692,14 @@ interface FragmentModule {
@IntoMap
@FragmentKey(SpacePeopleFragment::class)
fun bindSpacePeopleFragment(fragment: SpacePeopleFragment): Fragment
@Binds
@IntoMap
@FragmentKey(SpaceSettingsFragment::class)
fun bindSpaceSettingsFragment(fragment: SpaceSettingsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(SpaceManageRoomsFragment::class)
fun bindSpaceManageRoomsFragment(fragment: SpaceManageRoomsFragment): Fragment
}
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.core.epoxy
import android.widget.CompoundButton
import com.google.android.material.switchmaterial.SwitchMaterial
import com.google.android.material.textfield.TextInputEditText
fun VectorEpoxyHolder.setValueOnce(textInputEditText: TextInputEditText, value: String?) {
if (view.isAttachedToWindow) {
// the view is attached to the window
// So it is a rebind of new data and you could ignore it assuming this is text that was already inputted into the view.
// Downside is if you ever wanted to programmatically change the content of the edit text while it is on screen you would not be able to
} else {
textInputEditText.setText(value)
}
}
fun VectorEpoxyHolder.setValueOnce(switchView: SwitchMaterial, switchChecked: Boolean, listener: CompoundButton.OnCheckedChangeListener) {
if (view.isAttachedToWindow) {
// the view is attached to the window
// So it is a rebind of new data and you could ignore it assuming this is value that was already inputted into the view.
} else {
switchView.isChecked = switchChecked
switchView.setOnCheckedChangeListener(listener)
}
}
......@@ -57,15 +57,3 @@ fun EditText.setupAsSearch(@DrawableRes searchIconRes: Int = R.drawable.ic_searc
return@OnTouchListener false
})
}
/**
* Update the edit text value, only if necessary and move the cursor to the end of the text
*/
fun EditText.setTextSafe(value: String?) {
if (value != null && text.toString() != value) {
setText(value)
// To fix jumping cursor to the start https://github.com/airbnb/epoxy/issues/426
// Note: there is still a known bug if deleting char in the middle of the text, by long pressing on the backspace button.
setSelection(value.length)
}
}
......@@ -16,12 +16,14 @@
package im.vector.app.core.ui.list
import android.widget.TextView
import androidx.annotation.ColorInt
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.features.themes.ThemeUtils
/**
* A generic list item header left aligned with notice color.
......@@ -32,9 +34,18 @@ abstract class GenericItemHeader : VectorEpoxyModel<GenericItemHeader.Holder>()
@EpoxyAttribute
var text: String? = null
@EpoxyAttribute
@ColorInt
var textColor: Int? = null
override fun bind(holder: Holder) {
super.bind(holder)
holder.text.setTextOrHide(text)
if (textColor != null) {
holder.text.setTextColor(textColor!!)
} else {
holder.text.setTextColor(ThemeUtils.getColor(holder.view.context, R.attr.vctr_notice_text_color))
}
}
class Holder : VectorEpoxyHolder() {
......
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.core.ui.list
import android.content.res.ColorStateList
import android.view.Gravity
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.core.view.isVisible
import androidx.core.widget.ImageViewCompat
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.features.themes.ThemeUtils
/**
* A generic list item with a rounded corner background and an optional icon
*/
@EpoxyModelClass(layout = R.layout.item_generic_pill_footer)
abstract class GenericPillItem : VectorEpoxyModel<GenericPillItem.Holder>() {
@EpoxyAttribute
var text: CharSequence? = null
@EpoxyAttribute
var style: ItemStyle = ItemStyle.NORMAL_TEXT
@EpoxyAttribute
var itemClickAction: GenericItem.Action? = null
@EpoxyAttribute
var centered: Boolean = false
@EpoxyAttribute
@DrawableRes
var imageRes: Int? = null
@EpoxyAttribute
var tintIcon: Boolean = true
override fun bind(holder: Holder) {
super.bind(holder)
holder.textView.setTextOrHide(text)
holder.textView.typeface = style.toTypeFace()
holder.textView.textSize = style.toTextSize()
holder.textView.gravity = if (centered) Gravity.CENTER_HORIZONTAL else Gravity.START
if (imageRes != null) {
holder.imageView.setImageResource(imageRes!!)
holder.imageView.isVisible = true
} else {
holder.imageView.isVisible = false
}
if (tintIcon) {
val iconTintColor = ThemeUtils.getColor(holder.view.context, R.attr.riotx_text_secondary)
ImageViewCompat.setImageTintList(holder.imageView, ColorStateList.valueOf(iconTintColor))
} else {
ImageViewCompat.setImageTintList(holder.imageView, null)
}
holder.view.setOnClickListener {
itemClickAction?.perform?.run()
}
}
class Holder : VectorEpoxyHolder() {
val imageView by bind<ImageView>(R.id.itemGenericPillImage)
val textView by bind<TextView>(R.id.itemGenericPillText)
}
}
......@@ -27,7 +27,7 @@ import com.google.android.material.textfield.TextInputLayout
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.extensions.setTextSafe
import im.vector.app.core.epoxy.setValueOnce
import im.vector.app.core.platform.SimpleTextWatcher
@EpoxyModelClass(layout = R.layout.item_form_text_input)
......@@ -60,7 +60,7 @@ abstract class FormEditTextItem : VectorEpoxyModel<FormEditTextItem.Holder>() {
@EpoxyAttribute
var endIconMode: Int? = null
@EpoxyAttribute
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var onTextChange: ((String) -> Unit)? = null
private val onTextChangeListener = object : SimpleTextWatcher() {
......@@ -76,8 +76,8 @@ abstract class FormEditTextItem : VectorEpoxyModel<FormEditTextItem.Holder>() {
holder.textInputLayout.error = errorMessage
holder.textInputLayout.endIconMode = endIconMode ?: TextInputLayout.END_ICON_NONE
// Update only if text is different and value is not null
holder.textInputEditText.setTextSafe(value)
holder.setValueOnce(holder.textInputEditText, value)
holder.textInputEditText.isEnabled = enabled
inputType?.let { holder.textInputEditText.inputType = it }
holder.textInputEditText.isSingleLine = singleLine ?: false
......
......@@ -26,7 +26,7 @@ import com.google.android.material.textfield.TextInputLayout
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.extensions.setTextSafe
import im.vector.app.core.epoxy.setValueOnce
import im.vector.app.core.platform.SimpleTextWatcher
@EpoxyModelClass(layout = R.layout.item_form_text_input_with_button)
......@@ -61,8 +61,8 @@ abstract class FormEditTextWithButtonItem : VectorEpoxyModel<FormEditTextWithBut
holder.textInputLayout.isEnabled = enabled
holder.textInputLayout.hint = hint
// Update only if text is different
holder.textInputEditText.setTextSafe(value)
holder.setValueOnce(holder.textInputEditText, value)
holder.textInputEditText.isEnabled = enabled
holder.textInputEditText.addTextChangedListener(onTextChangeListener)
......
......@@ -83,7 +83,6 @@ abstract class FormEditableSquareAvatarItem : EpoxyModelWithHolder<FormEditableS
override fun unbind(holder: Holder) {
avatarRenderer?.clear(holder.image)
GlideApp.with(holder.image).clear(holder.image)
super.unbind(holder)
}
......
......@@ -27,7 +27,7 @@ import com.google.android.material.textfield.TextInputLayout
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.extensions.setTextSafe
import im.vector.app.core.epoxy.setValueOnce
import im.vector.app.core.platform.SimpleTextWatcher
@EpoxyModelClass(layout = R.layout.item_form_multiline_text_input)
......@@ -57,7 +57,7 @@ abstract class FormMultiLineEditTextItem : VectorEpoxyModel<FormMultiLineEditTex
@EpoxyAttribute
var typeFace: Typeface = Typeface.DEFAULT
@EpoxyAttribute
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var onTextChange: ((String) -> Unit)? = null
private val onTextChangeListener = object : SimpleTextWatcher() {
......@@ -76,8 +76,8 @@ abstract class FormMultiLineEditTextItem : VectorEpoxyModel<FormMultiLineEditTex
holder.textInputEditText.textSize = textSizeSp?.toFloat() ?: 14f
holder.textInputEditText.minLines = minLines
// Update only if text is different and value is not null
holder.textInputEditText.setTextSafe(value)
holder.setValueOnce(holder.textInputEditText, value)
holder.textInputEditText.isEnabled = enabled
holder.textInputEditText.addTextChangedListener(onTextChangeListener)
......
......@@ -25,6 +25,7 @@ import com.google.android.material.switchmaterial.SwitchMaterial
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.setValueOnce
import im.vector.app.core.extensions.setTextOrHide
@EpoxyModelClass(layout = R.layout.item_form_switch)
......@@ -40,7 +41,7 @@ abstract class FormSwitchItem : VectorEpoxyModel<FormSwitchItem.Holder>() {
var switchChecked: Boolean = false
@EpoxyAttribute
var title: String? = null
var title: CharSequence? = null
@EpoxyAttribute
var summary: String? = null
......@@ -61,11 +62,10 @@ abstract class FormSwitchItem : VectorEpoxyModel<FormSwitchItem.Holder>() {
holder.switchView.isEnabled = enabled
holder.switchView.setOnCheckedChangeListener(null)
holder.switchView.isChecked = switchChecked
holder.switchView.setOnCheckedChangeListener { _, isChecked ->
holder.setValueOnce(holder.switchView, switchChecked) { _, isChecked ->
listener?.invoke(isChecked)
}
holder.divider.isVisible = showDivider
}
......
......@@ -18,6 +18,7 @@
package im.vector.app.features.grouplist
import android.content.res.ColorStateList
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
......@@ -35,8 +36,9 @@ import im.vector.app.features.themes.ThemeUtils
abstract class HomeSpaceSummaryItem : VectorEpoxyModel<HomeSpaceSummaryItem.Holder>() {
@EpoxyAttribute var selected: Boolean = false
@EpoxyAttribute var listener: (() -> Unit)? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: (() -> Unit)? = null
@EpoxyAttribute var countState : UnreadCounterBadgeView.State = UnreadCounterBadgeView.State(0, false)
@EpoxyAttribute var showSeparator: Boolean = false
override fun getViewType(): Int {
// mm.. it's reusing the same layout for basic space item
......@@ -56,6 +58,7 @@ abstract class HomeSpaceSummaryItem : VectorEpoxyModel<HomeSpaceSummaryItem.Hold
holder.leaveView.isVisible = false
holder.counterBadgeView.render(countState)
holder.bottomSeparator.isVisible = showSeparator
}
class Holder : VectorEpoxyHolder() {
......@@ -64,5 +67,6 @@ abstract class HomeSpaceSummaryItem : VectorEpoxyModel<HomeSpaceSummaryItem.Hold
val rootView by bind<CheckableConstraintLayout>(R.id.itemGroupLayout)
val leaveView by bind<ImageView>(R.id.groupTmpLeave)
val counterBadgeView by bind<UnreadCounterBadgeView>(R.id.groupCounterBadge)
val bottomSeparator by bind<View>(R.id.groupBottomSeparator)
}
}
......@@ -55,6 +55,7 @@ import im.vector.app.features.permalink.PermalinkHandler
import im.vector.app.features.popup.DefaultVectorAlert
import im.vector.app.features.popup.PopupAlertManager
import im.vector.app.features.popup.VerificationVectorAlert
import im.vector.app.features.rageshake.ReportType
import im.vector.app.features.rageshake.VectorUncaughtExceptionHandler
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.VectorSettingsActivity
......@@ -216,6 +217,9 @@ class HomeActivity :
SpaceInviteBottomSheet.newInstance(sharedAction.spaceId)
.show(supportFragmentManager, "SPACE_INVITE")
}
HomeActivitySharedAction.SendSpaceFeedBack -> {
bugReporter.openBugReportScreen(this, ReportType.SPACE_BETA_FEEDBACK)
}
}.exhaustive
}
.disposeOnDestroy()
......@@ -449,11 +453,11 @@ class HomeActivity :
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_home_suggestion -> {
bugReporter.openBugReportScreen(this, true)
bugReporter.openBugReportScreen(this, ReportType.SUGGESTION)
return true
}
R.id.menu_home_report_bug -> {
bugReporter.openBugReportScreen(this, false)
bugReporter.openBugReportScreen(this, ReportType.BUG_REPORT)
return true
}
R.id.menu_home_init_sync_legacy -> {
......
......@@ -29,4 +29,5 @@ sealed class HomeActivitySharedAction : VectorSharedAction {
data class OpenSpacePreview(val spaceId: String) : HomeActivitySharedAction()
data class OpenSpaceInvite(val spaceId: String) : HomeActivitySharedAction()
data class ShowSpaceSettings(val spaceId: String) : HomeActivitySharedAction()
object SendSpaceFeedBack : HomeActivitySharedAction()
}
......@@ -202,7 +202,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
setState {
copy(
notificationCountCatchup = dmRooms.totalCount + otherRooms.totalCount + roomsInvite + dmInvites,
notificationHighlightCatchup = dmRooms.isHighlight || otherRooms.isHighlight,
notificationHighlightCatchup = dmRooms.isHighlight || otherRooms.isHighlight || (dmInvites + roomsInvite) > 0,
notificationCountPeople = dmRooms.totalCount + dmInvites,
notificationHighlightPeople = dmRooms.isHighlight || dmInvites > 0,
notificationCountRooms = otherRooms.totalCount + roomsInvite,
......
......@@ -29,6 +29,7 @@ import im.vector.app.RoomGroupingMethod
import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.settings.VectorPreferences
import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
import org.matrix.android.sdk.api.query.ActiveSpaceFilter
......@@ -52,6 +53,7 @@ data class CountInfo(
class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initialState: UnreadMessagesState,
session: Session,
private val vectorPreferences: VectorPreferences,
appStateHandler: AppStateHandler)
: VectorViewModel<UnreadMessagesState, EmptyAction, EmptyViewEvents>(initialState) {
......@@ -126,12 +128,24 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initia
}
is RoomGroupingMethod.BySpace -> {
val selectedSpace = appStateHandler.safeActiveSpaceId()
val counts = session.getNotificationCountForRooms(
val inviteCount = session.getRoomSummaries(
roomSummaryQueryParams { this.memberships = listOf(Membership.INVITE) }
).size
val totalCount = session.getNotificationCountForRooms(
roomSummaryQueryParams {
this.memberships = listOf(Membership.JOIN)
this.activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(null)
this.activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(null).takeIf {
vectorPreferences.labsSpacesOnlyOrphansInHome()
} ?: ActiveSpaceFilter.None
}
)
val counts = RoomAggregateNotificationCount(
totalCount.notificationCount + inviteCount,
totalCount.highlightCount + inviteCount
)
val rootCounts = session.spaceService().getRootSpaceSummaries()
.filter {
// filter out current selection
......
......@@ -832,7 +832,7 @@ class RoomDetailViewModel @AssistedInject constructor(
session.spaceService().getSpace(spaceId)
?.addChildren(
state.roomId,
listOf(session.sessionParams.homeServerHost ?: ""),
null,
null,
true
)
......@@ -849,7 +849,7 @@ class RoomDetailViewModel @AssistedInject constructor(
session.spaceService().getSpace(slashCommandResult.spaceId)
?.addChildren(
room.roomId,
listOf(session.sessionParams.homeServerHost ?: ""),
null,
null,
false
)
......
......@@ -62,6 +62,7 @@ class TimelineControllerInterceptorHelper(private val positionOfReadMarker: KMut
val firstUnreadEventId = (unreadState as? UnreadState.HasUnread)?.firstUnreadEventId
var atLeastOneVisibleItemSinceLastDaySeparator = false
var atLeastOneVisibleItemsBeforeReadMarker = false
var appendReadMarker = false
// Then iterate on models so we have the exact positions in the adapter
modelsIterator.forEach { epoxyModel ->
......@@ -72,11 +73,7 @@ class TimelineControllerInterceptorHelper(private val positionOfReadMarker: KMut
}
epoxyModel.getEventIds().forEach { eventId ->
adapterPositionMapping[eventId] = index
if (epoxyModel.canAppendReadMarker() && eventId == firstUnreadEventId && atLeastOneVisibleItemsBeforeReadMarker) {
modelsIterator.addReadMarkerItem(callback)
index++
positionOfReadMarker.set(index)
}
appendReadMarker = epoxyModel.canAppendReadMarker() && eventId == firstUnreadEventId && atLeastOneVisibleItemsBeforeReadMarker
}
}
if (epoxyModel is DaySeparatorItem) {
......@@ -91,6 +88,12 @@ class TimelineControllerInterceptorHelper(private val positionOfReadMarker: KMut
atLeastOneVisibleItemSinceLastDaySeparator = true
}
}
if (appendReadMarker) {
modelsIterator.addReadMarkerItem(callback)
index++
positionOfReadMarker.set(index)
appendReadMarker = false
}
index++
}
previousModelsSize = models.size
......@@ -103,8 +106,6 @@ class TimelineControllerInterceptorHelper(private val positionOfReadMarker: KMut
it.setOnVisibilityStateChanged(ReadMarkerVisibilityStateChangedListener(callback))
}
add(readMarker)
// Use next as we still have some process to do before the next iterator loop
next()
}
private fun MutableListIterator<EpoxyModel<*>>.removeCallItemIfNeeded(
......
......@@ -69,12 +69,12 @@ class RoomListViewModel @Inject constructor(
* Filter the rooms if they are part of the current space (children and grand children).
* If current space is null, will return orphan rooms only
*/
NORMAL,
ORPHANS_IF_SPACE_NULL,
/**
* Special case when we don't want to discriminate rooms when current space is null.
* In this case return all.
*/
NOT_IF_ALL,
ALL_IF_SPACE_NULL,
/** Do not filter based on space*/
NONE
}
......@@ -131,7 +131,8 @@ class RoomListViewModel @Inject constructor(
},
{
updatableQuery = it
}
},
vectorPreferences.labsSpacesOnlyOrphansInHome()
).buildSections(initialState.displayMode)
} else {
GroupRoomListSectionBuilder(
......
......@@ -50,6 +50,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) lateinit var lastFormattedEvent: CharSequence
@EpoxyAttribute lateinit var lastEventTime: CharSequence
@EpoxyAttribute var encryptionTrustLevel: RoomEncryptionTrustLevel? = null
@EpoxyAttribute var izPublic: Boolean = false
@EpoxyAttribute var unreadNotificationCount: Int = 0
@EpoxyAttribute var hasUnreadMessage: Boolean = false
@EpoxyAttribute var hasDraft: Boolean = false
......@@ -74,6 +75,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
holder.draftView.isVisible = hasDraft
avatarRenderer.render(matrixItem, holder.avatarImageView)
holder.roomAvatarDecorationImageView.render(encryptionTrustLevel)
holder.roomAvatarPublicDecorationImageView.isVisible = izPublic
holder.roomAvatarFailSendingImageView.isVisible = hasFailedSending
renderSelection(holder, showSelected)
holder.typingView.setTextOrHide(typingMessage)
......@@ -110,6 +112,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
val avatarCheckedImageView by bind<ImageView>(R.id.roomAvatarCheckedImageView)
val avatarImageView by bind<ImageView>(R.id.roomAvatarImageView)
val roomAvatarDecorationImageView by bind<ShieldImageView>(R.id.roomAvatarDecorationImageView)
val roomAvatarPublicDecorationImageView by bind<ImageView>(R.id.roomAvatarPublicDecorationImageView)
val roomAvatarFailSendingImageView by bind<ImageView>(R.id.roomAvatarFailSendingImageView)
val rootView by bind<ViewGroup>(R.id.itemRoomLayout)
}
......