forked from GitHub-Mirror/riotX-android
Refactoring / Use view ViewStub to avoid layout xml duplication
This commit is contained in:
@ -20,6 +20,7 @@ import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.AnyThread
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
@ -77,31 +78,35 @@ object AvatarRenderer {
|
||||
|
||||
@AnyThread
|
||||
fun getPlaceholderDrawable(context: Context, identifier: String, text: String): Drawable {
|
||||
val avatarColor = ContextCompat.getColor(context, getAvatarColor(identifier))
|
||||
val avatarColor = ContextCompat.getColor(context, getColorFromUserId(identifier))
|
||||
return if (text.isEmpty()) {
|
||||
TextDrawable.builder().buildRound("", avatarColor)
|
||||
} else {
|
||||
val isUserId = MatrixPatterns.isUserId(text)
|
||||
val firstLetterIndex = if (isUserId) 1 else 0
|
||||
val firstLetter = text[firstLetterIndex].toString().toUpperCase()
|
||||
TextDrawable.builder().buildRound(firstLetter, avatarColor)
|
||||
TextDrawable.builder()
|
||||
.beginConfig()
|
||||
.bold()
|
||||
.endConfig()
|
||||
.buildRound(firstLetter, avatarColor)
|
||||
}
|
||||
}
|
||||
|
||||
// PRIVATE API *********************************************************************************
|
||||
|
||||
|
||||
private fun getAvatarColor(text: String? = null): Int {
|
||||
var colorIndex: Long = 0
|
||||
if (!text.isNullOrEmpty()) {
|
||||
var sum: Long = 0
|
||||
for (i in 0 until text.length) {
|
||||
sum += text[i].toLong()
|
||||
}
|
||||
colorIndex = sum % AVATAR_COLOR_LIST.size
|
||||
}
|
||||
return AVATAR_COLOR_LIST[colorIndex.toInt()]
|
||||
}
|
||||
// private fun getAvatarColor(text: String? = null): Int {
|
||||
// var colorIndex: Long = 0
|
||||
// if (!text.isNullOrEmpty()) {
|
||||
// var sum: Long = 0
|
||||
// for (i in 0 until text.length) {
|
||||
// sum += text[i].toLong()
|
||||
// }
|
||||
// colorIndex = sum % AVATAR_COLOR_LIST.size
|
||||
// }
|
||||
// return AVATAR_COLOR_LIST[colorIndex.toInt()]
|
||||
// }
|
||||
|
||||
private fun buildGlideRequest(glideRequest: GlideRequests, avatarUrl: String?): GlideRequest<Drawable> {
|
||||
val resolvedUrl = Matrix.getInstance().currentSession!!.contentUrlResolver()
|
||||
@ -112,4 +117,32 @@ object AvatarRenderer {
|
||||
.apply(RequestOptions.circleCropTransform())
|
||||
}
|
||||
|
||||
|
||||
//Based on riot-web implementation
|
||||
@ColorRes
|
||||
fun getColorFromUserId(sender: String): Int {
|
||||
var hash = 0
|
||||
var i = 0
|
||||
var chr: Char
|
||||
if (sender.isEmpty()) {
|
||||
return R.color.username_1
|
||||
}
|
||||
while (i < sender.length) {
|
||||
chr = sender[i]
|
||||
hash = (hash shl 5) - hash + chr.toInt()
|
||||
hash = hash or 0
|
||||
i++
|
||||
}
|
||||
val cI = Math.abs(hash) % 8 + 1
|
||||
return when (cI) {
|
||||
1 -> R.color.username_1
|
||||
2 -> R.color.username_2
|
||||
3 -> R.color.username_3
|
||||
4 -> R.color.username_4
|
||||
5 -> R.color.username_5
|
||||
6 -> R.color.username_6
|
||||
7 -> R.color.username_7
|
||||
else -> R.color.username_8
|
||||
}
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@ import im.vector.riotredesign.core.extensions.localDateTime
|
||||
import im.vector.riotredesign.core.linkify.VectorLinkify
|
||||
import im.vector.riotredesign.core.resources.ColorProvider
|
||||
import im.vector.riotredesign.core.utils.DebouncedClickListener
|
||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
|
||||
@ -70,7 +71,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
|
||||
val avatarUrl = event.senderAvatar
|
||||
val memberName = event.senderName ?: event.root.sender ?: ""
|
||||
val formattedMemberName = span(memberName) {
|
||||
textColor = colorProvider.getColor(getColorFor(event.root.sender ?: ""))
|
||||
textColor = colorProvider.getColor(AvatarRenderer.getColorFromUserId(event.root.sender ?: ""))
|
||||
}
|
||||
val informationData = MessageInformationData(eventId = eventId,
|
||||
senderId = event.root.sender ?: "",
|
||||
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.riotredesign.features.home.room.detail.timeline.item
|
||||
|
||||
import android.content.Context
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.view.ViewStub
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.constraintlayout.widget.Guideline
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
|
||||
|
||||
abstract class AEventItemBase<H : AEventItemBase.BaseHolder> : VectorEpoxyModel<H>() {
|
||||
|
||||
var avatarStyle: AvatarStyle = Companion.AvatarStyle.MEDIUM
|
||||
|
||||
override fun bind(holder: H) {
|
||||
super.bind(holder)
|
||||
//optimize?
|
||||
val px = dpToPx(avatarStyle.avatarSizeDP, holder.view.context)
|
||||
holder.leftGuideline.setGuidelineBegin(px)
|
||||
}
|
||||
|
||||
|
||||
override fun getViewType(): Int {
|
||||
return getStubType()
|
||||
}
|
||||
|
||||
abstract fun getStubType(): Int
|
||||
|
||||
|
||||
abstract class BaseHolder : VectorEpoxyHolder() {
|
||||
|
||||
val leftGuideline by bind<Guideline>(R.id.messageStartGuideline)
|
||||
|
||||
@IdRes
|
||||
abstract fun getStubId(): Int
|
||||
|
||||
override fun bindView(itemView: View) {
|
||||
super.bindView(itemView)
|
||||
inflateStub()
|
||||
}
|
||||
|
||||
private fun inflateStub() {
|
||||
view.findViewById<ViewStub>(getStubId()).inflate()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
enum class AvatarStyle(val avatarSizeDP: Int) {
|
||||
BIG(50),
|
||||
MEDIUM(40),
|
||||
SMALL(30),
|
||||
NONE(0)
|
||||
}
|
||||
|
||||
fun dpToPx(dp: Int, context: Context): Int {
|
||||
return TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
dp.toFloat(),
|
||||
context.resources.displayMetrics
|
||||
).toInt()
|
||||
}
|
||||
}
|
||||
}
|
@ -19,13 +19,15 @@ package im.vector.riotredesign.features.home.room.detail.timeline.item
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import im.vector.riotredesign.R
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.jakewharton.rxbinding2.view.RxView
|
||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||
|
||||
abstract class AbsMessageItem<H : AbsMessageItem.Holder> : VectorEpoxyModel<H>() {
|
||||
|
||||
abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AEventItemBase<H>() {
|
||||
|
||||
abstract val informationData: MessageInformationData
|
||||
|
||||
@ -41,6 +43,14 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : VectorEpoxyModel<H>()
|
||||
override fun bind(holder: H) {
|
||||
super.bind(holder)
|
||||
if (informationData.showInformation) {
|
||||
|
||||
val lp = holder.avatarImageView.layoutParams?.apply {
|
||||
val size = dpToPx(avatarStyle.avatarSizeDP, holder.view.context)
|
||||
height = size
|
||||
width = size
|
||||
}
|
||||
holder.avatarImageView.layoutParams = lp
|
||||
|
||||
holder.avatarImageView.visibility = View.VISIBLE
|
||||
holder.avatarImageView.setOnClickListener(avatarClickListener)
|
||||
holder.memberNameView.visibility = View.VISIBLE
|
||||
@ -63,10 +73,11 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : VectorEpoxyModel<H>()
|
||||
alpha = if (informationData.sendState.isSent()) 1f else 0.5f
|
||||
}
|
||||
|
||||
abstract class Holder : VectorEpoxyHolder() {
|
||||
abstract val avatarImageView: ImageView
|
||||
abstract val memberNameView: TextView
|
||||
abstract val timeView: TextView
|
||||
abstract class Holder : BaseHolder() {
|
||||
|
||||
val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView)
|
||||
val memberNameView by bind<TextView>(R.id.messageMemberNameView)
|
||||
val timeView by bind<TextView>(R.id.messageTimeView)
|
||||
}
|
||||
|
||||
}
|
@ -20,19 +20,26 @@ import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_default)
|
||||
abstract class DefaultItem : VectorEpoxyModel<DefaultItem.Holder>() {
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base_noinfo)
|
||||
abstract class DefaultItem : AEventItemBase<DefaultItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute var text: CharSequence? = null
|
||||
@EpoxyAttribute
|
||||
var text: CharSequence? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
holder.messageView.text = text
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
override fun getStubType(): Int = STUB_ID
|
||||
|
||||
class Holder : BaseHolder() {
|
||||
override fun getStubId(): Int = STUB_ID
|
||||
|
||||
val messageView by bind<TextView>(R.id.stateMessageView)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val STUB_ID = R.id.messageContentDefaultStub
|
||||
}
|
||||
}
|
@ -24,15 +24,14 @@ import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.children
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||
|
||||
|
||||
data class MergedHeaderItem(private val isCollapsed: Boolean,
|
||||
private val mergeId: String,
|
||||
private val mergeData: List<Data>,
|
||||
private val onCollapsedStateChanged: (Boolean) -> Unit
|
||||
) : VectorEpoxyModel<MergedHeaderItem.Holder>() {
|
||||
private val mergeId: String,
|
||||
private val mergeData: List<Data>,
|
||||
private val onCollapsedStateChanged: (Boolean) -> Unit
|
||||
) : AEventItemBase<MergedHeaderItem.Holder>() {
|
||||
|
||||
private val distinctMergeData = mergeData.distinctBy { it.userId }
|
||||
|
||||
@ -41,13 +40,15 @@ data class MergedHeaderItem(private val isCollapsed: Boolean,
|
||||
}
|
||||
|
||||
override fun getDefaultLayout(): Int {
|
||||
return R.layout.item_timeline_event_merged_header
|
||||
return R.layout.item_timeline_event_base_noinfo
|
||||
}
|
||||
|
||||
override fun createNewHolder(): Holder {
|
||||
return Holder()
|
||||
}
|
||||
|
||||
override fun getStubType(): Int = STUB_ID
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.expandView.setOnClickListener {
|
||||
@ -84,11 +85,17 @@ data class MergedHeaderItem(private val isCollapsed: Boolean,
|
||||
val avatarUrl: String?
|
||||
)
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
class Holder : BaseHolder() {
|
||||
override fun getStubId(): Int = STUB_ID
|
||||
|
||||
val expandView by bind<TextView>(R.id.itemMergedExpandTextView)
|
||||
val summaryView by bind<TextView>(R.id.itemMergedSummaryTextView)
|
||||
val separatorView by bind<View>(R.id.itemMergedSeparatorView)
|
||||
val avatarListView by bind<ViewGroup>(R.id.itemMergedAvatarListView)
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val STUB_ID = R.id.messageContentMergedheaderStub
|
||||
}
|
||||
}
|
@ -26,13 +26,18 @@ import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotredesign.R
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_file_message)
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
||||
abstract class MessageFileItem : AbsMessageItem<MessageFileItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute var filename: CharSequence = ""
|
||||
@EpoxyAttribute @DrawableRes var iconRes: Int = 0
|
||||
@EpoxyAttribute override lateinit var informationData: MessageInformationData
|
||||
@EpoxyAttribute var clickListener: View.OnClickListener? = null
|
||||
@EpoxyAttribute
|
||||
var filename: CharSequence = ""
|
||||
@EpoxyAttribute
|
||||
@DrawableRes
|
||||
var iconRes: Int = 0
|
||||
@EpoxyAttribute
|
||||
override lateinit var informationData: MessageInformationData
|
||||
@EpoxyAttribute
|
||||
var clickListener: View.OnClickListener? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
@ -43,15 +48,19 @@ abstract class MessageFileItem : AbsMessageItem<MessageFileItem.Holder>() {
|
||||
holder.filenameView.paintFlags = (holder.filenameView.paintFlags or Paint.UNDERLINE_TEXT_FLAG)
|
||||
}
|
||||
|
||||
override fun getStubType(): Int = STUB_ID
|
||||
|
||||
class Holder : AbsMessageItem.Holder() {
|
||||
override val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView)
|
||||
override val memberNameView by bind<TextView>(R.id.messageMemberNameView)
|
||||
override val timeView by bind<TextView>(R.id.messageTimeView)
|
||||
override fun getStubId(): Int = STUB_ID
|
||||
|
||||
val fileLayout by bind<ViewGroup>(R.id.messageFileLayout)
|
||||
val fileImageView by bind<ImageView>(R.id.messageFileImageView)
|
||||
val filenameView by bind<TextView>(R.id.messageFilenameView)
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val STUB_ID = R.id.messageContentFileStub
|
||||
}
|
||||
|
||||
}
|
@ -19,20 +19,23 @@ package im.vector.riotredesign.features.home.room.detail.timeline.item
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
|
||||
import im.vector.riotredesign.features.media.ImageContentRenderer
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_image_video_message)
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
||||
abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute lateinit var mediaData: ImageContentRenderer.Data
|
||||
@EpoxyAttribute override lateinit var informationData: MessageInformationData
|
||||
@EpoxyAttribute var playable: Boolean = false
|
||||
@EpoxyAttribute var clickListener: View.OnClickListener? = null
|
||||
@EpoxyAttribute
|
||||
lateinit var mediaData: ImageContentRenderer.Data
|
||||
@EpoxyAttribute
|
||||
override lateinit var informationData: MessageInformationData
|
||||
@EpoxyAttribute
|
||||
var playable: Boolean = false
|
||||
@EpoxyAttribute
|
||||
var clickListener: View.OnClickListener? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
@ -49,13 +52,21 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
|
||||
super.unbind(holder)
|
||||
}
|
||||
|
||||
override fun getStubType(): Int = STUB_ID
|
||||
|
||||
class Holder : AbsMessageItem.Holder() {
|
||||
override val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView)
|
||||
override val memberNameView by bind<TextView>(R.id.messageMemberNameView)
|
||||
override val timeView by bind<TextView>(R.id.messageTimeView)
|
||||
|
||||
override fun getStubId(): Int = STUB_ID
|
||||
|
||||
val progressLayout by bind<ViewGroup>(R.id.messageMediaUploadProgressLayout)
|
||||
val imageView by bind<ImageView>(R.id.messageThumbnailView)
|
||||
val playContentView by bind<ImageView>(R.id.messageMediaPlayView)
|
||||
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private val STUB_ID = R.id.messageContentMediaStub
|
||||
}
|
||||
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
|
||||
package im.vector.riotredesign.features.home.room.detail.timeline.item
|
||||
|
||||
import android.text.Spannable
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
@ -33,7 +34,7 @@ import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_text_message)
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
||||
abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
@ -67,12 +68,12 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun getStubType(): Int = R.id.messageContentTextStub
|
||||
|
||||
class Holder : AbsMessageItem.Holder() {
|
||||
override val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView)
|
||||
override val memberNameView by bind<TextView>(R.id.messageMemberNameView)
|
||||
override val timeView by bind<TextView>(R.id.messageTimeView)
|
||||
val messageView by bind<AppCompatTextView>(R.id.messageTextView)
|
||||
override fun getStubId(): Int = R.id.messageContentTextStub
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -22,30 +22,41 @@ import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_notice)
|
||||
abstract class NoticeItem : VectorEpoxyModel<NoticeItem.Holder>() {
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base_noinfo)
|
||||
abstract class NoticeItem : AEventItemBase<NoticeItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute var noticeText: CharSequence? = null
|
||||
@EpoxyAttribute var avatarUrl: String? = null
|
||||
@EpoxyAttribute var userId: String = ""
|
||||
@EpoxyAttribute var memberName: CharSequence? = null
|
||||
@EpoxyAttribute
|
||||
var noticeText: CharSequence? = null
|
||||
@EpoxyAttribute
|
||||
var avatarUrl: String? = null
|
||||
@EpoxyAttribute
|
||||
var userId: String = ""
|
||||
@EpoxyAttribute
|
||||
var memberName: CharSequence? = null
|
||||
|
||||
|
||||
@EpoxyAttribute
|
||||
var longClickListener: View.OnLongClickListener? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.noticeTextView.text = noticeText
|
||||
AvatarRenderer.render(avatarUrl, userId, memberName?.toString(), holder.avatarImageView)
|
||||
holder.view.setOnLongClickListener(longClickListener)
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
override fun getStubType(): Int = STUB_ID
|
||||
|
||||
class Holder : BaseHolder() {
|
||||
override fun getStubId(): Int = STUB_ID
|
||||
|
||||
val avatarImageView by bind<ImageView>(R.id.itemNoticeAvatarView)
|
||||
val noticeTextView by bind<TextView>(R.id.itemNoticeTextView)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val STUB_ID = R.id.messageContentNoticeStub
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user