kopia lustrzana https://github.com/ryukoposting/Signal-Android
Fix leak in Message Details for disappearing messages.
rodzic
96ea4c0cc2
commit
4215b0391d
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue