Fix leak in Message Details for disappearing messages.

fork-5.53.8
Cody Henthorne 2022-06-28 19:34:52 -04:00
rodzic 96ea4c0cc2
commit 4215b0391d
3 zmienionych plików z 56 dodań i 104 usunięć

Wyświetl plik

@ -16,17 +16,12 @@ import org.thoughtcrime.securesms.conversation.colors.Colorizer;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.mms.GlideRequests;
import java.util.List;
final class MessageDetailsAdapter extends ListAdapter<MessageDetailsAdapter.MessageDetailsViewState<?>, RecyclerView.ViewHolder> {
private static final Object EXPIRATION_TIMER_CHANGE_PAYLOAD = new Object();
private final LifecycleOwner lifecycleOwner;
private final GlideRequests glideRequests;
private final Colorizer colorizer;
private Callbacks callbacks;
private boolean running;
private final Callbacks callbacks;
MessageDetailsAdapter(@NonNull LifecycleOwner lifecycleOwner, @NonNull GlideRequests glideRequests, @NonNull Colorizer colorizer, @NonNull Callbacks callbacks) {
super(new MessageDetailsDiffer());
@ -34,7 +29,6 @@ final class MessageDetailsAdapter extends ListAdapter<MessageDetailsAdapter.Mess
this.glideRequests = glideRequests;
this.colorizer = colorizer;
this.callbacks = callbacks;
this.running = true;
}
@Override
@ -54,7 +48,7 @@ final class MessageDetailsAdapter extends ListAdapter<MessageDetailsAdapter.Mess
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (holder instanceof MessageHeaderViewHolder) {
((MessageHeaderViewHolder) holder).bind(lifecycleOwner, (ConversationMessage) getItem(position).data, running);
((MessageHeaderViewHolder) holder).bind(lifecycleOwner, (ConversationMessage) getItem(position).data);
} else if (holder instanceof RecipientHeaderViewHolder) {
((RecipientHeaderViewHolder) holder).bind((RecipientHeader) getItem(position).data);
} else if (holder instanceof RecipientViewHolder) {
@ -64,34 +58,11 @@ final class MessageDetailsAdapter extends ListAdapter<MessageDetailsAdapter.Mess
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position, @NonNull List<Object> payloads) {
if (payloads.isEmpty()) {
super.onBindViewHolder(holder, position, payloads);
} else if (holder instanceof MessageHeaderViewHolder) {
((MessageHeaderViewHolder) holder).partialBind((ConversationMessage) getItem(position).data, running);
}
}
@Override
public int getItemViewType(int position) {
return getItem(position).itemType;
}
void resumeMessageExpirationTimer() {
running = true;
if (getItemCount() > 0) {
notifyItemChanged(0, EXPIRATION_TIMER_CHANGE_PAYLOAD);
}
}
void pauseMessageExpirationTimer() {
running = false;
if (getItemCount() > 0) {
notifyItemChanged(0, EXPIRATION_TIMER_CHANGE_PAYLOAD);
}
}
private static class MessageDetailsDiffer extends DiffUtil.ItemCallback<MessageDetailsViewState<?>> {
@Override
public boolean areItemsTheSame(@NonNull MessageDetailsViewState<?> oldItem, @NonNull MessageDetailsViewState<?> newItem) {

Wyświetl plik

@ -76,18 +76,6 @@ public final class MessageDetailsFragment extends FullScreenDialogFragment {
initializeVideoPlayer(view);
}
@Override
public void onResume() {
super.onResume();
adapter.resumeMessageExpirationTimer();
}
@Override
public void onPause() {
super.onPause();
adapter.pauseMessageExpirationTimer();
}
@Override
public void onDismiss(@NonNull DialogInterface dialog) {
super.onDismiss(dialog);
@ -104,7 +92,7 @@ public final class MessageDetailsFragment extends FullScreenDialogFragment {
View toolbarShadow = view.findViewById(R.id.toolbar_shadow);
colorizer = new Colorizer();
adapter = new MessageDetailsAdapter(this, glideRequests, colorizer, this::onErrorClicked);
adapter = new MessageDetailsAdapter(getViewLifecycleOwner(), glideRequests, colorizer, this::onErrorClicked);
recyclerViewColorizer = new RecyclerViewColorizer(list);
list.setAdapter(adapter);

Wyświetl plik

@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.messagedetails;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.CountDownTimer;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.StyleSpan;
@ -13,12 +14,12 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.exoplayer2.MediaItem;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.conversation.ConversationItem;
@ -40,23 +41,24 @@ import java.text.SimpleDateFormat;
import java.util.HashSet;
import java.util.Locale;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
final class MessageHeaderViewHolder extends RecyclerView.ViewHolder implements GiphyMp4Playable, Colorizable {
private final TextView sentDate;
private final TextView receivedDate;
private final TextView expiresIn;
private final TextView transport;
private final TextView errorText;
private final View resendButton;
private final View messageMetadata;
private final ViewStub updateStub;
private final ViewStub sentStub;
private final ViewStub receivedStub;
private final Colorizer colorizer;
private final TextView sentDate;
private final TextView receivedDate;
private final TextView expiresIn;
private final TextView transport;
private final TextView errorText;
private final View resendButton;
private final View messageMetadata;
private final ViewStub updateStub;
private final ViewStub sentStub;
private final ViewStub receivedStub;
private final Colorizer colorizer;
private final GlideRequests glideRequests;
private GlideRequests glideRequests;
private ConversationItem conversationItem;
private ExpiresUpdater expiresUpdater;
private ConversationItem conversationItem;
private CountDownTimer expiresUpdater;
MessageHeaderViewHolder(@NonNull View itemView, GlideRequests glideRequests, @NonNull Colorizer colorizer) {
super(itemView);
@ -75,20 +77,16 @@ final class MessageHeaderViewHolder extends RecyclerView.ViewHolder implements G
receivedStub = itemView.findViewById(R.id.message_details_header_message_view_received_multimedia);
}
void bind(@NonNull LifecycleOwner lifecycleOwner, @Nullable ConversationMessage conversationMessage, boolean running) {
void bind(@NonNull LifecycleOwner lifecycleOwner, @NonNull ConversationMessage conversationMessage) {
MessageRecord messageRecord = conversationMessage.getMessageRecord();
bindMessageView(lifecycleOwner, conversationMessage);
bindErrorState(messageRecord);
bindSentReceivedDates(messageRecord);
bindExpirationTime(messageRecord, running);
bindExpirationTime(lifecycleOwner, messageRecord);
bindTransport(messageRecord);
}
void partialBind(ConversationMessage conversationMessage, boolean running) {
bindExpirationTime(conversationMessage.getMessageRecord(), running);
}
private void bindMessageView(@NonNull LifecycleOwner lifecycleOwner, @Nullable ConversationMessage conversationMessage) {
private void bindMessageView(@NonNull LifecycleOwner lifecycleOwner, @NonNull ConversationMessage conversationMessage) {
if (conversationItem == null) {
if (conversationMessage.getMessageRecord().isGroupAction()) {
conversationItem = (ConversationItem) updateStub.inflate();
@ -145,7 +143,7 @@ final class MessageHeaderViewHolder extends RecyclerView.ViewHolder implements G
sentDate.setText(formatBoldString(R.string.message_details_header_sent, "-"));
receivedDate.setVisibility(View.GONE);
} else {
Locale dateLocale = Locale.getDefault();
Locale dateLocale = Locale.getDefault();
SimpleDateFormat dateFormatter = DateUtils.getDetailedDateFormatter(itemView.getContext(), dateLocale);
sentDate.setText(formatBoldString(R.string.message_details_header_sent, dateFormatter.format(new Date(messageRecord.getDateSent()))));
sentDate.setOnLongClickListener(v -> {
@ -166,9 +164,9 @@ final class MessageHeaderViewHolder extends RecyclerView.ViewHolder implements G
}
}
private void bindExpirationTime(final MessageRecord messageRecord, boolean running) {
private void bindExpirationTime(@NonNull LifecycleOwner lifecycleOwner, @NonNull MessageRecord messageRecord) {
if (expiresUpdater != null) {
expiresUpdater.stop();
expiresUpdater.cancel();
expiresUpdater = null;
}
@ -178,10 +176,36 @@ final class MessageHeaderViewHolder extends RecyclerView.ViewHolder implements G
}
expiresIn.setVisibility(View.VISIBLE);
if (running) {
expiresUpdater = new ExpiresUpdater(messageRecord);
ThreadUtil.runOnMain(expiresUpdater);
}
lifecycleOwner.getLifecycle().addObserver(new DefaultLifecycleObserver() {
@Override
public void onResume(@NonNull LifecycleOwner owner) {
if (expiresUpdater != null) {
expiresUpdater.cancel();
}
expiresUpdater = new CountDownTimer(messageRecord.getExpiresIn(), TimeUnit.SECONDS.toMillis(1)) {
@Override
public void onTick(long millisUntilFinished) {
int expirationTime = Math.max((int) (TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished)), 1);
String duration = ExpirationUtil.getExpirationDisplayValue(itemView.getContext(), expirationTime);
expiresIn.setText(formatBoldString(R.string.message_details_header_disappears, duration));
}
@Override
public void onFinish() {}
};
expiresUpdater.start();
}
@Override
public void onPause(@NonNull LifecycleOwner owner) {
if (expiresUpdater != null) {
expiresUpdater.cancel();
expiresUpdater = null;
}
}
});
}
private void bindTransport(MessageRecord messageRecord) {
@ -255,35 +279,4 @@ final class MessageHeaderViewHolder extends RecyclerView.ViewHolder implements G
public @NonNull ProjectionList getColorizerProjections(@NonNull ViewGroup coordinateRoot) {
return conversationItem.getColorizerProjections(coordinateRoot);
}
private class ExpiresUpdater implements Runnable {
private final long expireStartedTimestamp;
private final long expiresInTimestamp;
private boolean running;
ExpiresUpdater(MessageRecord messageRecord) {
expireStartedTimestamp = messageRecord.getExpireStarted();
expiresInTimestamp = messageRecord.getExpiresIn();
running = true;
}
@Override
public void run() {
long elapsed = System.currentTimeMillis() - expireStartedTimestamp;
long remaining = expiresInTimestamp - elapsed;
int expirationTime = Math.max((int) (remaining / 1000), 1);
String duration = ExpirationUtil.getExpirationDisplayValue(itemView.getContext(), expirationTime);
expiresIn.setText(formatBoldString(R.string.message_details_header_disappears, duration));
if (running && expirationTime > 1) {
ThreadUtil.runOnMainDelayed(this, 500);
}
}
void stop() {
running = false;
}
}
}