From ea242f6737a819a665661391605ef47d3d2c14ab Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 28 Aug 2019 17:17:37 +0200 Subject: [PATCH 1/3] Hide ReadReceipt View when it is not relevant --- .../java/im/vector/riotx/core/ui/views/ReadReceiptsView.kt | 4 ---- .../features/home/room/detail/timeline/item/AbsMessageItem.kt | 2 -- .../features/home/room/detail/timeline/item/BaseEventItem.kt | 2 ++ .../features/home/room/detail/timeline/item/DefaultItem.kt | 4 ++++ .../home/room/detail/timeline/item/MergedHeaderItem.kt | 4 ++++ .../features/home/room/detail/timeline/item/NoticeItem.kt | 2 -- vector/src/main/res/layout/item_timeline_event_base.xml | 4 +++- .../src/main/res/layout/item_timeline_event_base_noinfo.xml | 4 +++- 8 files changed, 16 insertions(+), 10 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/core/ui/views/ReadReceiptsView.kt b/vector/src/main/java/im/vector/riotx/core/ui/views/ReadReceiptsView.kt index 44d1ee6f..15a54248 100644 --- a/vector/src/main/java/im/vector/riotx/core/ui/views/ReadReceiptsView.kt +++ b/vector/src/main/java/im/vector/riotx/core/ui/views/ReadReceiptsView.kt @@ -21,11 +21,8 @@ import android.util.AttributeSet import android.view.View import android.widget.ImageView import android.widget.LinearLayout -import androidx.core.view.isInvisible import androidx.core.view.isVisible -import butterknife.ButterKnife import im.vector.riotx.R -import im.vector.riotx.core.utils.DebouncedClickListener import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData import kotlinx.android.synthetic.main.view_read_receipts.view.* @@ -48,7 +45,6 @@ class ReadReceiptsView @JvmOverloads constructor( private fun setupView() { inflate(context, R.layout.view_read_receipts, this) - ButterKnife.bind(this) } fun render(readReceipts: List, avatarRenderer: AvatarRenderer, clickListener: OnClickListener) { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/AbsMessageItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/AbsMessageItem.kt index cf5f2517..2a4142e4 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/AbsMessageItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/AbsMessageItem.kt @@ -32,7 +32,6 @@ import com.airbnb.epoxy.EpoxyAttribute import im.vector.matrix.android.api.session.room.send.SendState import im.vector.riotx.R import im.vector.riotx.core.resources.ColorProvider -import im.vector.riotx.core.ui.views.ReadReceiptsView import im.vector.riotx.core.utils.DebouncedClickListener import im.vector.riotx.core.utils.DimensionUtils.dpToPx import im.vector.riotx.features.home.AvatarRenderer @@ -181,7 +180,6 @@ abstract class AbsMessageItem : BaseEventItem() { val avatarImageView by bind(R.id.messageAvatarImageView) val memberNameView by bind(R.id.messageMemberNameView) val timeView by bind(R.id.messageTimeView) - val readReceiptsView by bind(R.id.readReceiptsView) var reactionWrapper: ViewGroup? = null var reactionFlowHelper: Flow? = null } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/BaseEventItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/BaseEventItem.kt index 843f52b3..96625d16 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/BaseEventItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/BaseEventItem.kt @@ -24,6 +24,7 @@ import im.vector.riotx.R import im.vector.riotx.core.epoxy.VectorEpoxyHolder import im.vector.riotx.core.epoxy.VectorEpoxyModel import im.vector.riotx.core.platform.CheckableView +import im.vector.riotx.core.ui.views.ReadReceiptsView import im.vector.riotx.core.utils.DimensionUtils.dpToPx /** @@ -49,6 +50,7 @@ abstract class BaseEventItem : VectorEpoxyModel abstract class BaseHolder(@IdRes val stubId: Int) : VectorEpoxyHolder() { val leftGuideline by bind(R.id.messageStartGuideline) val checkableBackground by bind(R.id.messageSelectedBackground) + val readReceiptsView by bind(R.id.readReceiptsView) override fun bindView(itemView: View) { super.bindView(itemView) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt index 0b30facf..e43c0a95 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt @@ -17,6 +17,7 @@ package im.vector.riotx.features.home.room.detail.timeline.item import android.widget.TextView +import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.riotx.R @@ -29,6 +30,9 @@ abstract class DefaultItem : BaseEventItem() { override fun bind(holder: Holder) { holder.messageView.text = text + + // TODO We should handle read receipt here as well + holder.readReceiptsView.isVisible = false } override fun getViewType() = STUB_ID diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MergedHeaderItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MergedHeaderItem.kt index 4f26f9bb..03dd5a8a 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MergedHeaderItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MergedHeaderItem.kt @@ -21,6 +21,7 @@ import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import androidx.core.view.children +import androidx.core.view.isVisible import im.vector.riotx.R import im.vector.riotx.features.home.AvatarRenderer @@ -75,6 +76,9 @@ data class MergedHeaderItem(private val isCollapsed: Boolean, holder.separatorView.visibility = View.VISIBLE holder.expandView.setText(R.string.merged_events_collapse) } + + // No read receipt for this item + holder.readReceiptsView.isVisible = false } data class Data( diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/NoticeItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/NoticeItem.kt index 51a7b0ce..29850497 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/NoticeItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/NoticeItem.kt @@ -22,7 +22,6 @@ import android.widget.TextView import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.riotx.R -import im.vector.riotx.core.ui.views.ReadReceiptsView import im.vector.riotx.core.utils.DebouncedClickListener import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController @@ -72,7 +71,6 @@ abstract class NoticeItem : BaseEventItem() { class Holder : BaseHolder(STUB_ID) { val avatarImageView by bind(R.id.itemNoticeAvatarView) val noticeTextView by bind(R.id.itemNoticeTextView) - val readReceiptsView by bind(R.id.readReceiptsView) } companion object { diff --git a/vector/src/main/res/layout/item_timeline_event_base.xml b/vector/src/main/res/layout/item_timeline_event_base.xml index 2f0be78f..8e42804c 100644 --- a/vector/src/main/res/layout/item_timeline_event_base.xml +++ b/vector/src/main/res/layout/item_timeline_event_base.xml @@ -129,8 +129,10 @@ android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:layout_marginBottom="4dp" + android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" /> + app:layout_constraintEnd_toEndOf="parent" + tools:visibility="visible" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/item_timeline_event_base_noinfo.xml b/vector/src/main/res/layout/item_timeline_event_base_noinfo.xml index 77268399..3f82c8db 100644 --- a/vector/src/main/res/layout/item_timeline_event_base_noinfo.xml +++ b/vector/src/main/res/layout/item_timeline_event_base_noinfo.xml @@ -58,8 +58,10 @@ android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:layout_marginBottom="4dp" + android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" /> + app:layout_constraintEnd_toEndOf="parent" + tools:visibility="visible" /> \ No newline at end of file From ef0362ba9cbf8eb41be177b6b7676343c370f80a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 28 Aug 2019 17:31:31 +0200 Subject: [PATCH 2/3] Display Read Receipt on unsupported events --- .../timeline/factory/DefaultItemFactory.kt | 17 +++++++++++++-- .../timeline/factory/TimelineItemFactory.kt | 9 ++------ .../room/detail/timeline/item/DefaultItem.kt | 21 ++++++++++++++++--- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/DefaultItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/DefaultItemFactory.kt index 05e4007e..9adbfb8a 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/DefaultItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/DefaultItemFactory.kt @@ -17,21 +17,34 @@ package im.vector.riotx.features.home.room.detail.timeline.factory import im.vector.matrix.android.api.session.room.timeline.TimelineEvent +import im.vector.riotx.features.home.AvatarRenderer +import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController import im.vector.riotx.features.home.room.detail.timeline.item.DefaultItem import im.vector.riotx.features.home.room.detail.timeline.item.DefaultItem_ +import im.vector.riotx.features.home.room.detail.timeline.util.MessageInformationDataFactory import javax.inject.Inject -class DefaultItemFactory @Inject constructor(){ +class DefaultItemFactory @Inject constructor(private val avatarRenderer: AvatarRenderer, + private val informationDataFactory: MessageInformationDataFactory) { - fun create(event: TimelineEvent, highlight: Boolean, exception: Exception? = null): DefaultItem? { + fun create(event: TimelineEvent, + highlight: Boolean, + callback: TimelineEventController.Callback?, + exception: Exception? = null): DefaultItem? { val text = if (exception == null) { "${event.root.getClearType()} events are not yet handled" } else { "an exception occurred when rendering the event ${event.root.eventId}" } + + val informationData = informationDataFactory.create(event, null) + return DefaultItem_() .text(text) + .avatarRenderer(avatarRenderer) .highlighted(highlight) + .informationData(informationData) + .readReceiptsCallback(callback) } } \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/TimelineItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/TimelineItemFactory.kt index b1ae595e..e9ce37f2 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/TimelineItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/TimelineItemFactory.kt @@ -20,12 +20,7 @@ import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.riotx.core.epoxy.EmptyItem_ import im.vector.riotx.core.epoxy.VectorEpoxyModel -import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController -import im.vector.riotx.features.home.room.detail.timeline.helper.senderAvatar -import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData -import im.vector.riotx.features.home.room.detail.timeline.item.NoticeItem_ -import im.vector.riotx.features.home.room.detail.timeline.util.MessageInformationDataFactory import timber.log.Timber import javax.inject.Inject @@ -71,7 +66,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me // Unhandled event types (yet) EventType.STATE_ROOM_THIRD_PARTY_INVITE, - EventType.STICKER -> defaultItemFactory.create(event, highlight) + EventType.STICKER -> defaultItemFactory.create(event, highlight, callback) else -> { Timber.v("Type ${event.root.getClearType()} not handled") null @@ -79,7 +74,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me } } catch (e: Exception) { Timber.e(e, "failed to create message item") - defaultItemFactory.create(event, highlight, e) + defaultItemFactory.create(event, highlight, callback, e) } return (computedModel ?: EmptyItem_()) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt index e43c0a95..ea1aa064 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt @@ -16,23 +16,38 @@ package im.vector.riotx.features.home.room.detail.timeline.item +import android.view.View import android.widget.TextView -import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.riotx.R +import im.vector.riotx.core.utils.DebouncedClickListener +import im.vector.riotx.features.home.AvatarRenderer +import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController @EpoxyModelClass(layout = R.layout.item_timeline_event_base_noinfo) abstract class DefaultItem : BaseEventItem() { + @EpoxyAttribute + lateinit var informationData: MessageInformationData + + @EpoxyAttribute + lateinit var avatarRenderer: AvatarRenderer + + @EpoxyAttribute + var readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null + + private val _readReceiptsClickListener = DebouncedClickListener(View.OnClickListener { + readReceiptsCallback?.onReadReceiptsClicked(informationData.readReceipts) + }) + @EpoxyAttribute var text: CharSequence? = null override fun bind(holder: Holder) { holder.messageView.text = text - // TODO We should handle read receipt here as well - holder.readReceiptsView.isVisible = false + holder.readReceiptsView.render(informationData.readReceipts, avatarRenderer, _readReceiptsClickListener) } override fun getViewType() = STUB_ID From c95223f5d24188363a4529d883ddf83ec88c72cf Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 28 Aug 2019 18:17:37 +0200 Subject: [PATCH 3/3] Add long click support on unsupported event --- .../room/detail/timeline/factory/DefaultItemFactory.kt | 1 + .../home/room/detail/timeline/item/DefaultItem.kt | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/DefaultItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/DefaultItemFactory.kt index 9adbfb8a..ec7d9e16 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/DefaultItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/DefaultItemFactory.kt @@ -44,6 +44,7 @@ class DefaultItemFactory @Inject constructor(private val avatarRenderer: AvatarR .avatarRenderer(avatarRenderer) .highlighted(highlight) .informationData(informationData) + .baseCallback(callback) .readReceiptsCallback(callback) } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt index ea1aa064..b4919494 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt @@ -34,6 +34,13 @@ abstract class DefaultItem : BaseEventItem() { @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer + @EpoxyAttribute + var baseCallback: TimelineEventController.BaseCallback? = null + + private var longClickListener = View.OnLongClickListener { + return@OnLongClickListener baseCallback?.onEventLongClicked(informationData, null, it) == true + } + @EpoxyAttribute var readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null @@ -47,6 +54,7 @@ abstract class DefaultItem : BaseEventItem() { override fun bind(holder: Holder) { holder.messageView.text = text + holder.view.setOnLongClickListener(longClickListener) holder.readReceiptsView.render(informationData.readReceipts, avatarRenderer, _readReceiptsClickListener) }