Fix conversation memory leak.

This commit is contained in:
Cody Henthorne 2024-06-10 14:54:02 -04:00 committed by GitHub
parent 65dc0d3f34
commit 057ffdbaaf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 17 additions and 63 deletions

View file

@ -7,6 +7,7 @@ package org.thoughtcrime.securesms.conversation.v2.items
import android.net.Uri
import android.view.View
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.Observer
import com.bumptech.glide.RequestManager
import io.mockk.mockk
@ -203,8 +204,8 @@ class V2ConversationItemShapeTest {
private val colorizer = Colorizer()
override val lifecycleOwner: LifecycleOwner = mockk(relaxed = true)
override val displayMode: ConversationItemDisplayMode = ConversationItemDisplayMode.Standard
override val clickListener: ConversationAdapter.ItemClickListener = FakeConversationItemClickListener
override val selectedItems: Set<MultiselectPart> = emptySet()
override val isMessageRequestAccepted: Boolean = true

View file

@ -66,7 +66,7 @@ import java.util.Locale
import java.util.Optional
class ConversationAdapterV2(
private val lifecycleOwner: LifecycleOwner,
override val lifecycleOwner: LifecycleOwner,
override val requestManager: RequestManager,
override val clickListener: ItemClickListener,
private var hasWallpaper: Boolean,
@ -356,34 +356,6 @@ class ConversationAdapterV2(
}
}
private inner class OutgoingTextOnlyViewHolder(itemView: View) : ConversationViewHolder<OutgoingTextOnly>(itemView) {
override fun bind(model: OutgoingTextOnly) {
bindable.setEventListener(clickListener)
if (bindPayloadsIfAvailable()) {
return
}
bindable.bind(
lifecycleOwner,
model.conversationMessage,
previousMessage,
nextMessage,
requestManager,
Locale.getDefault(),
_selected,
model.conversationMessage.threadRecipient,
searchQuery,
false,
hasWallpaper && displayMode.displayWallpaper(),
isMessageRequestAccepted,
model.conversationMessage == inlineContent,
colorizer,
displayMode
)
}
}
private inner class OutgoingMediaViewHolder(itemView: View) : ConversationViewHolder<OutgoingMedia>(itemView) {
override fun bind(model: OutgoingMedia) {
bindable.setEventListener(clickListener)
@ -413,34 +385,6 @@ class ConversationAdapterV2(
}
}
private inner class IncomingTextOnlyViewHolder(itemView: View) : ConversationViewHolder<IncomingTextOnly>(itemView) {
override fun bind(model: IncomingTextOnly) {
bindable.setEventListener(clickListener)
if (bindPayloadsIfAvailable()) {
return
}
bindable.bind(
lifecycleOwner,
model.conversationMessage,
previousMessage,
nextMessage,
requestManager,
Locale.getDefault(),
_selected,
model.conversationMessage.threadRecipient,
searchQuery,
false,
hasWallpaper && displayMode.displayWallpaper(),
isMessageRequestAccepted,
model.conversationMessage == inlineContent,
colorizer,
displayMode
)
}
}
private inner class IncomingMediaViewHolder(itemView: View) : ConversationViewHolder<IncomingMedia>(itemView) {
override fun bind(model: IncomingMedia) {
bindable.setEventListener(clickListener)

View file

@ -5,6 +5,7 @@
package org.thoughtcrime.securesms.conversation.v2.items
import androidx.lifecycle.LifecycleOwner
import com.bumptech.glide.RequestManager
import org.thoughtcrime.securesms.conversation.ConversationAdapter
import org.thoughtcrime.securesms.conversation.ConversationItemDisplayMode
@ -17,6 +18,7 @@ import org.thoughtcrime.securesms.database.model.MessageRecord
* visible to an inner class.
*/
interface V2ConversationContext {
val lifecycleOwner: LifecycleOwner
val requestManager: RequestManager
val displayMode: ConversationItemDisplayMode
val clickListener: ConversationAdapter.ItemClickListener

View file

@ -24,6 +24,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.RecyclerView
import org.signal.core.util.StringUtil
import org.signal.core.util.dp
@ -44,7 +45,6 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.util.InterceptableLongClickCopyLinkSpan
import org.thoughtcrime.securesms.util.LongClickMovementMethod
@ -70,7 +70,7 @@ open class V2ConversationItemTextOnlyViewHolder<Model : MappingModel<Model>>(
private val binding: V2ConversationItemTextOnlyBindingBridge,
private val conversationContext: V2ConversationContext,
footerDelegate: V2FooterPositionDelegate = V2FooterPositionDelegate(binding)
) : V2ConversationItemViewHolder<Model>(binding.root, conversationContext), Multiselectable, InteractiveConversationElement, RecipientForeverObserver {
) : V2ConversationItemViewHolder<Model>(binding.root, conversationContext), Multiselectable, InteractiveConversationElement, Observer<Recipient> {
companion object {
private val STYLE_FACTORY = SearchUtil.StyleFactory { arrayOf<CharacterStyle>(BackgroundColorSpan(Color.YELLOW), ForegroundColorSpan(Color.BLACK)) }
@ -210,12 +210,12 @@ open class V2ConversationItemTextOnlyViewHolder<Model : MappingModel<Model>>(
check(model is ConversationMessageElement)
if (this::conversationMessage.isInitialized) {
conversationMessage.messageRecord.fromRecipient.live().removeForeverObserver(this)
conversationMessage.messageRecord.fromRecipient.live().removeObserver(this)
}
conversationMessage = model.conversationMessage
if (conversationMessage.threadRecipient.isGroup) {
conversationMessage.messageRecord.fromRecipient.live().observeForever(this)
conversationMessage.messageRecord.fromRecipient.live().observe(conversationContext.lifecycleOwner, this)
}
shape = shapeDelegate.setMessageShape(
@ -790,7 +790,7 @@ open class V2ConversationItemTextOnlyViewHolder<Model : MappingModel<Model>>(
}
}
override fun onRecipientChanged(recipient: Recipient) {
override fun onChanged(recipient: Recipient) {
presentSender()
}
}

View file

@ -102,6 +102,13 @@ public final class LiveRecipient {
ThreadUtil.postToMain(() -> observableLiveData.observe(owner, observer));
}
/**
* Removes observer of this data.
*/
public void removeObserver(@NonNull Observer<Recipient> observer) {
ThreadUtil.runOnMain(() -> observableLiveData.removeObserver(observer));
}
/**
* Removes all observers of this data registered for the given LifecycleOwner.
*/