diff --git a/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageDetailsAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageDetailsAdapter.java index e37ce339c..dc1d7b01f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageDetailsAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageDetailsAdapter.java @@ -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, 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 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> { @Override public boolean areItemsTheSame(@NonNull MessageDetailsViewState oldItem, @NonNull MessageDetailsViewState newItem) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageDetailsFragment.java b/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageDetailsFragment.java index bfabd61f8..26c9f874f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageDetailsFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageDetailsFragment.java @@ -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); diff --git a/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageHeaderViewHolder.java b/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageHeaderViewHolder.java index 16f0a2dd9..cfa9c5e90 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageHeaderViewHolder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messagedetails/MessageHeaderViewHolder.java @@ -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; - } - } }