Fix pool limits and y-translation issues with CFv2 recycler view.

This commit is contained in:
Cody Henthorne
2023-06-07 12:51:08 -04:00
committed by GitHub
parent d6a03df087
commit 7e0e6c2786
6 changed files with 101 additions and 6 deletions

View File

@@ -10,6 +10,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.core.text.HtmlCompat
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
import com.google.android.exoplayer2.MediaItem
import org.signal.core.util.logging.Log
import org.signal.core.util.toOptional
@@ -74,7 +75,7 @@ class ConversationAdapterV2(
private val condensedMode: ConversationItemDisplayMode? = null
init {
registerFactory(ThreadHeader::class.java, ::ThreadHeaderViewHolder, R.layout.conversation_item_banner)
registerFactory(ThreadHeader::class.java, ::ThreadHeaderViewHolder, R.layout.conversation_item_thread_header)
registerFactory(ConversationUpdate::class.java) { parent ->
val view = CachedInflater.from(parent.context).inflate<View>(R.layout.conversation_item_update, parent, false)
@@ -102,6 +103,27 @@ class ConversationAdapterV2(
}
}
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
for ((model, type) in itemTypes) {
val count: Int = when (model) {
ThreadHeader::class.java -> 1
ConversationUpdate::class.java -> 5
OutgoingTextOnly::class.java -> 25
OutgoingMedia::class.java -> 15
IncomingTextOnly::class.java -> 25
IncomingMedia::class.java -> 15
Placeholder::class.java -> 5
else -> 0
}
if (count > 0) {
recyclerView.recycledViewPool.setMaxRecycledViews(type, count)
}
}
}
/** [messagePosition] is one-based index and adapter is zero-based. */
fun getAdapterPositionForMessagePosition(messagePosition: Int): Int {
return messagePosition - 1

View File

@@ -13,9 +13,11 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.Rect
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
@@ -38,6 +40,7 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.Toolbar
import androidx.core.app.ActivityCompat
import androidx.core.app.ActivityOptionsCompat
import androidx.core.content.ContextCompat
@@ -114,6 +117,7 @@ import org.thoughtcrime.securesms.contactshare.SharedContactDetailsActivity
import org.thoughtcrime.securesms.conversation.AttachmentKeyboardButton
import org.thoughtcrime.securesms.conversation.BadDecryptLearnMoreDialog
import org.thoughtcrime.securesms.conversation.ConversationAdapter
import org.thoughtcrime.securesms.conversation.ConversationHeaderView
import org.thoughtcrime.securesms.conversation.ConversationIntents
import org.thoughtcrime.securesms.conversation.ConversationIntents.ConversationScreenType
import org.thoughtcrime.securesms.conversation.ConversationItem
@@ -321,6 +325,7 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
private lateinit var attachmentManager: AttachmentManager
private lateinit var multiselectItemDecoration: MultiselectItemDecoration
private lateinit var openableGiftItemDecoration: OpenableGiftItemDecoration
private lateinit var threadHeaderMarginDecoration: ThreadHeaderMarginDecoration
private var animationsAllowed = false
private var actionMode: ActionMode? = null
@@ -394,6 +399,8 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
.addTo(disposables)
container.fragmentManager = childFragmentManager
ToolbarDependentMarginListener(binding.toolbar)
}
override fun onResume() {
@@ -422,6 +429,11 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
EventBus.getDefault().unregister(this)
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
ToolbarDependentMarginListener(binding.toolbar)
}
override fun onDestroyView() {
super.onDestroyView()
if (pinnedShortcutReceiver != null) {
@@ -861,7 +873,6 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
adapter::getAdapterPositionForMessagePosition
)
ConversationAdapter.initializePool(binding.conversationItemRecycler.recycledViewPool)
adapter.setPagingController(viewModel.pagingController)
recyclerViewColorizer = RecyclerViewColorizer(binding.conversationItemRecycler)
@@ -895,6 +906,9 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
true
}
)
threadHeaderMarginDecoration = ThreadHeaderMarginDecoration()
binding.conversationItemRecycler.addItemDecoration(threadHeaderMarginDecoration)
}
private fun initializeGiphyMp4(): GiphyMp4ProjectionRecycler {
@@ -2654,4 +2668,30 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
}
//endregion
private inner class ToolbarDependentMarginListener(private val toolbar: Toolbar) : ViewTreeObserver.OnGlobalLayoutListener {
init {
toolbar.viewTreeObserver.addOnGlobalLayoutListener(this)
}
override fun onGlobalLayout() {
val rect = Rect()
toolbar.getGlobalVisibleRect(rect)
threadHeaderMarginDecoration.toolbarMargin = rect.bottom + 16.dp
binding.conversationItemRecycler.invalidateItemDecorations()
toolbar.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
}
private inner class ThreadHeaderMarginDecoration : RecyclerView.ItemDecoration() {
var toolbarMargin: Int = 0
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)
if (view is ConversationHeaderView) {
outRect.top = toolbarMargin
}
}
}
}

View File

@@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.giph.mp4
import android.graphics.Canvas
import android.graphics.Rect
import androidx.core.view.children
import androidx.recyclerview.widget.RecyclerView
import org.thoughtcrime.securesms.conversation.ConversationAdapter
@@ -27,18 +28,28 @@ class GiphyMp4ItemDecoration(
parent.translationY = 0f
onRecyclerVerticalTranslationSet(parent.translationY)
} else {
val footerViewHolder = parent.children
val threadHeaderViewHolder = parent.children
.map { parent.getChildViewHolder(it) }
.filter { it is ConversationAdapter.FooterViewHolder || it is ConversationAdapterV2.ThreadHeaderViewHolder }
.firstOrNull()
if (footerViewHolder == null) {
if (threadHeaderViewHolder == null) {
parent.translationY = 0f
onRecyclerVerticalTranslationSet(parent.translationY)
return
}
val childTop: Int = footerViewHolder.itemView.top
val toolbarMargin = if (threadHeaderViewHolder is ConversationAdapterV2.ThreadHeaderViewHolder) {
// A decorator adds the margin for the toolbar, margin is difference of the bounds "height" and the view height
val bounds = Rect()
parent.getDecoratedBoundsWithMargins(threadHeaderViewHolder.itemView, bounds)
bounds.bottom - bounds.top - threadHeaderViewHolder.itemView.height
} else {
// Deprecated not needed for CFv2
0
}
val childTop: Int = threadHeaderViewHolder.itemView.top - toolbarMargin
parent.translationY = min(0, -childTop).toFloat()
onRecyclerVerticalTranslationSet(parent.translationY)
}

View File

@@ -89,6 +89,10 @@ public class MappingAdapter extends ListAdapter<MappingModel<?>, MappingViewHold
registerFactory(clazz, new LayoutFactory<>(creator, layout));
}
public Map<Class<?>, Integer> getItemTypes() {
return new HashMap<>(itemTypes);
}
@Override
public int getItemViewType(int position) {
Integer type = itemTypes.get(getItem(position).getClass());

View File

@@ -93,7 +93,7 @@ public class PagingMappingAdapter<Key> extends MappingAdapter {
return getItem(position) != null;
}
private static class Placeholder implements MappingModel<Placeholder> {
protected static class Placeholder implements MappingModel<Placeholder> {
@Override
public boolean areItemsTheSame(@NonNull Placeholder newItem) {
return false;