Remove observer when load completes or is cancelled.

This commit is contained in:
akwizgran
2025-05-02 15:03:20 +01:00
parent 9ecac899fb
commit 35d035b19f

View File

@@ -4,13 +4,11 @@ import android.animation.Animator;
import android.animation.ArgbEvaluator; import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.content.Context; import android.content.Context;
import android.text.util.Linkify;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator; import android.view.animation.AccelerateInterpolator;
import android.widget.TextView; import android.widget.TextView;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener; import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
import org.briarproject.briar.android.view.AuthorView; import org.briarproject.briar.android.view.AuthorView;
@@ -22,16 +20,20 @@ import androidx.annotation.CallSuper;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import static android.text.util.Linkify.WEB_URLS;
import static android.text.util.Linkify.addLinks;
import static androidx.core.content.ContextCompat.getColor; import static androidx.core.content.ContextCompat.getColor;
import static org.briarproject.bramble.util.StringUtils.trim; import static org.briarproject.bramble.util.StringUtils.trim;
import static org.briarproject.briar.android.util.UiUtils.makeLinksClickable; import static org.briarproject.briar.android.util.UiUtils.makeLinksClickable;
import static org.briarproject.nullsafety.NullSafety.requireNonNull;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
public abstract class BaseThreadItemViewHolder<I extends ThreadItem> public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
extends RecyclerView.ViewHolder { extends RecyclerView.ViewHolder implements Observer<String> {
private final static int ANIMATION_DURATION = 5000; private final static int ANIMATION_DURATION = 5000;
@@ -39,7 +41,9 @@ public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
private final ViewGroup layout; private final ViewGroup layout;
private final AuthorView author; private final AuthorView author;
@Nullable @Nullable
private MessageId boundMessageId = null; private ThreadItemListener<I> listener = null;
@Nullable
private LiveData<String> textLiveData = null;
public BaseThreadItemViewHolder(View v) { public BaseThreadItemViewHolder(View v) {
super(v); super(v);
@@ -52,10 +56,7 @@ public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
@CallSuper @CallSuper
public void bind(I item, LifecycleOwner lifecycleOwner, public void bind(I item, LifecycleOwner lifecycleOwner,
ThreadItemListener<I> listener) { ThreadItemListener<I> listener) {
boundMessageId = item.getId();
setText(item, lifecycleOwner, listener); setText(item, lifecycleOwner, listener);
Linkify.addLinks(textView, Linkify.WEB_URLS);
makeLinksClickable(textView, listener::onLinkClick);
author.setAuthor(item.getAuthor(), item.getAuthorInfo()); author.setAuthor(item.getAuthor(), item.getAuthorInfo());
author.setDate(item.getTimestamp()); author.setDate(item.getTimestamp());
@@ -72,14 +73,16 @@ public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
protected void setText(I item, LifecycleOwner lifecycleOwner, protected void setText(I item, LifecycleOwner lifecycleOwner,
ThreadItemListener<I> listener) { ThreadItemListener<I> listener) {
// Clear any existing text while we asynchronously load the new text
textView.setText(null); textView.setText(null);
LiveData<String> textLiveData = listener.loadItemText(item.getId()); // Remember the listener so we can use it to create links later
textLiveData.observe(lifecycleOwner, t -> { this.listener = listener;
// Check that the ViewHolder hasn't been re-bound while loading // If the view has been re-bound and we're already asynchronously
if (item.getId().equals(boundMessageId)) { // loading text for another item, stop observing it
textView.setText(trim(t)); if (textLiveData != null) textLiveData.removeObserver(this);
} // Asynchronously load the text for this item and observe the result
}); textLiveData = listener.loadItemText(item.getId());
textLiveData.observe(lifecycleOwner, this);
} }
private void animateFadeOut() { private void animateFadeOut() {
@@ -122,8 +125,23 @@ public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
} }
void onViewRecycled() { void onViewRecycled() {
// Reset the bound message ID so an asynchronous text loading task textView.setText(null);
// won't set the view's text while it's in the recycling pool if (textLiveData != null) {
boundMessageId = null; textLiveData.removeObserver(this);
textLiveData = null;
listener = null;
}
}
@Override
public void onChanged(String s) {
if (textLiveData != null) {
textLiveData.removeObserver(this);
textLiveData = null;
textView.setText(trim(s));
addLinks(textView, WEB_URLS);
makeLinksClickable(textView, requireNonNull(listener)::onLinkClick);
listener = null;
}
} }
} }