Suppress LayoutTransition during scroll events.

This commit is contained in:
Alex Hart
2023-09-07 11:26:45 -03:00
committed by GitHub
parent b410756dfd
commit 454b1f69ed
8 changed files with 71 additions and 5 deletions
@@ -217,6 +217,7 @@ class V2ConversationItemShapeTest {
override val isMessageRequestAccepted: Boolean = true override val isMessageRequestAccepted: Boolean = true
override val searchQuery: String? = null override val searchQuery: String? = null
override val glideRequests: GlideRequests = mockk() override val glideRequests: GlideRequests = mockk()
override val isParentInScroll: Boolean = false
override fun onStartExpirationTimeout(messageRecord: MessageRecord) = Unit override fun onStartExpirationTimeout(messageRecord: MessageRecord) = Unit
@@ -57,6 +57,10 @@ public interface BindableConversationItem extends Unbindable, GiphyMp4Playable,
void setEventListener(@Nullable EventListener listener); void setEventListener(@Nullable EventListener listener);
default void setParentScrolling(boolean isParentScrolling) {
// Intentionally Blank.
}
default void updateTimestamps() { default void updateTimestamps() {
// Intentionally Blank. // Intentionally Blank.
} }
@@ -16,6 +16,7 @@ interface ConversationAdapterBridge {
const val PAYLOAD_TIMESTAMP = 0 const val PAYLOAD_TIMESTAMP = 0
const val PAYLOAD_NAME_COLORS = 1 const val PAYLOAD_NAME_COLORS = 1
const val PAYLOAD_SELECTED = 2 const val PAYLOAD_SELECTED = 2
const val PAYLOAD_PARENT_SCROLLING = 3
} }
fun hasNoConversationMessages(): Boolean fun hasNoConversationMessages(): Boolean
@@ -418,6 +418,11 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
this.conversationRecipient.observeForever(this); this.conversationRecipient.observeForever(this);
} }
@Override
public void setParentScrolling(boolean isParentScrolling) {
bodyBubble.setParentScrolling(isParentScrolling);
}
@Override @Override
public void updateSelectedState() { public void updateSelectedState() {
setHasBeenQuoted(conversationMessage); setHasBeenQuoted(conversationMessage);
@@ -12,6 +12,7 @@ import androidx.annotation.Nullable;
import com.annimon.stream.Collectors; import com.annimon.stream.Collectors;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.components.Outliner; import org.thoughtcrime.securesms.components.Outliner;
import org.thoughtcrime.securesms.util.Projection; import org.thoughtcrime.securesms.util.Projection;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
@@ -30,19 +31,33 @@ public class ConversationItemBodyBubble extends LinearLayout {
private Projection quoteViewProjection; private Projection quoteViewProjection;
private Projection videoPlayerProjection; private Projection videoPlayerProjection;
private final BodyBubbleLayoutTransition bodyBubbleLayoutTransition = new BodyBubbleLayoutTransition();
public ConversationItemBodyBubble(Context context) { public ConversationItemBodyBubble(Context context) {
super(context); super(context);
setLayoutTransition(new BodyBubbleLayoutTransition()); init();
} }
public ConversationItemBodyBubble(Context context, @Nullable AttributeSet attrs) { public ConversationItemBodyBubble(Context context, @Nullable AttributeSet attrs) {
super(context, attrs); super(context, attrs);
setLayoutTransition(new BodyBubbleLayoutTransition()); init();
} }
public ConversationItemBodyBubble(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { public ConversationItemBodyBubble(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
setLayoutTransition(new BodyBubbleLayoutTransition()); init();
}
private void init() {
setLayoutTransition(bodyBubbleLayoutTransition);
}
public void setParentScrolling(boolean isParentScrolling) {
if (isParentScrolling) {
setLayoutTransition(null);
} else {
setLayoutTransition(bodyBubbleLayoutTransition);
}
} }
public void setOutliners(@NonNull List<Outliner> outliners) { public void setOutliners(@NonNull List<Outliner> outliners) {
@@ -90,6 +90,10 @@ class ConversationAdapterV2(
override var isMessageRequestAccepted: Boolean = false override var isMessageRequestAccepted: Boolean = false
override var isParentInScroll: Boolean = false
private val onScrollStateChangedListener = OnScrollStateChangedListener()
init { init {
registerFactory(ThreadHeader::class.java, ::ThreadHeaderViewHolder, R.layout.conversation_item_thread_header) registerFactory(ThreadHeader::class.java, ::ThreadHeaderViewHolder, R.layout.conversation_item_thread_header)
@@ -162,6 +166,8 @@ class ConversationAdapterV2(
recyclerView.recycledViewPool.setMaxRecycledViews(type, count) recyclerView.recycledViewPool.setMaxRecycledViews(type, count)
} }
} }
recyclerView.addOnScrollListener(onScrollStateChangedListener)
} }
override fun onViewRecycled(holder: MappingViewHolder<*>) { override fun onViewRecycled(holder: MappingViewHolder<*>) {
@@ -176,6 +182,8 @@ class ConversationAdapterV2(
.children .children
.filterIsInstance<Unbindable>() .filterIsInstance<Unbindable>()
.forEach { it.unbind() } .forEach { it.unbind() }
recyclerView.removeOnScrollListener(onScrollStateChangedListener)
} }
override val displayMode: ConversationItemDisplayMode override val displayMode: ConversationItemDisplayMode
@@ -498,6 +506,11 @@ class ConversationAdapterV2(
fun bindPayloadsIfAvailable(): Boolean { fun bindPayloadsIfAvailable(): Boolean {
var payloadApplied = false var payloadApplied = false
bindable.setParentScrolling(isParentInScroll)
if (payload.contains(ConversationAdapterBridge.PAYLOAD_PARENT_SCROLLING)) {
payloadApplied = true
}
if (payload.contains(ConversationAdapterBridge.PAYLOAD_TIMESTAMP)) { if (payload.contains(ConversationAdapterBridge.PAYLOAD_TIMESTAMP)) {
bindable.updateTimestamps() bindable.updateTimestamps()
payloadApplied = true payloadApplied = true
@@ -627,4 +640,14 @@ class ConversationAdapterV2(
} }
} }
} }
private inner class OnScrollStateChangedListener : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
val oldState = isParentInScroll
isParentInScroll = newState != RecyclerView.SCROLL_STATE_IDLE
if (isParentInScroll != oldState) {
notifyItemRangeChanged(0, itemCount, ConversationAdapterBridge.PAYLOAD_PARENT_SCROLLING)
}
}
}
} }
@@ -23,6 +23,7 @@ interface V2ConversationContext {
val selectedItems: Set<MultiselectPart> val selectedItems: Set<MultiselectPart>
val isMessageRequestAccepted: Boolean val isMessageRequestAccepted: Boolean
val searchQuery: String? val searchQuery: String?
val isParentInScroll: Boolean
fun onStartExpirationTimeout(messageRecord: MessageRecord) fun onStartExpirationTimeout(messageRecord: MessageRecord)
@@ -100,6 +100,7 @@ open class V2ConversationItemTextOnlyViewHolder<Model : MappingModel<Model>>(
private val bodyBubbleDrawable = ChatColorsDrawable() private val bodyBubbleDrawable = ChatColorsDrawable()
private val footerDrawable = ChatColorsDrawable() private val footerDrawable = ChatColorsDrawable()
private val bodyBubbleLayoutTransition = BodyBubbleLayoutTransition()
protected lateinit var shape: V2ConversationItemShape.MessageShape protected lateinit var shape: V2ConversationItemShape.MessageShape
@@ -155,7 +156,7 @@ open class V2ConversationItemTextOnlyViewHolder<Model : MappingModel<Model>>(
} }
binding.conversationItemBodyWrapper.background = bodyBubbleDrawable binding.conversationItemBodyWrapper.background = bodyBubbleDrawable
binding.conversationItemBodyWrapper.layoutTransition = BodyBubbleLayoutTransition() binding.conversationItemBodyWrapper.layoutTransition = bodyBubbleLayoutTransition
binding.conversationItemFooterBackground.background = footerDrawable binding.conversationItemFooterBackground.background = footerDrawable
} }
@@ -166,6 +167,22 @@ open class V2ConversationItemTextOnlyViewHolder<Model : MappingModel<Model>>(
} }
override fun bind(model: Model) { override fun bind(model: Model) {
var hasProcessedSupportedPayload = false
binding.conversationItemBodyWrapper.layoutTransition = if (conversationContext.isParentInScroll) {
null
} else {
bodyBubbleLayoutTransition
}
if (ConversationAdapterBridge.PAYLOAD_PARENT_SCROLLING in payload) {
if (payload.size == 1) {
return
} else {
hasProcessedSupportedPayload = true
}
}
check(model is ConversationMessageElement) check(model is ConversationMessageElement)
conversationMessage = model.conversationMessage conversationMessage = model.conversationMessage
@@ -176,7 +193,6 @@ open class V2ConversationItemTextOnlyViewHolder<Model : MappingModel<Model>>(
adapterPosition = bindingAdapterPosition adapterPosition = bindingAdapterPosition
) )
var hasProcessedSupportedPayload = false
if (ConversationAdapterBridge.PAYLOAD_TIMESTAMP in payload) { if (ConversationAdapterBridge.PAYLOAD_TIMESTAMP in payload) {
presentDate() presentDate()
hasProcessedSupportedPayload = true hasProcessedSupportedPayload = true