Add message editing feature.

This commit is contained in:
Clark
2023-04-14 16:29:26 -04:00
committed by Cody Henthorne
parent 4f06a0d27c
commit 07f6baf7c1
73 changed files with 2051 additions and 304 deletions

View File

@@ -18,7 +18,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class AnimatingToggle extends FrameLayout {
private View current;
private View previous;
private final Animation inAnimation;
private final Animation outAnimation;
@@ -55,9 +55,17 @@ public class AnimatingToggle extends FrameLayout {
public void display(@Nullable View view) {
if (view == current && current.getVisibility() == View.VISIBLE) return;
if (current != null) ViewUtil.animateOut(current, outAnimation, View.GONE);
if (view != null) ViewUtil.animateIn(view, inAnimation);
if (previous != null && previous.getAnimation() == outAnimation) {
previous.clearAnimation();
previous.setVisibility(View.GONE);
}
if (current != null) {
ViewUtil.animateOut(current, outAnimation, View.GONE);
}
if (view != null) {
ViewUtil.animateIn(view, inAnimation);
}
previous = current;
current = view;
}

View File

@@ -28,6 +28,7 @@ import com.airbnb.lottie.model.KeyPath;
import org.signal.core.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
import org.thoughtcrime.securesms.conversation.ConversationItemDisplayMode;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord;
@@ -35,7 +36,6 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.MessageRecordUtil;
import org.thoughtcrime.securesms.util.Projection;
@@ -143,8 +143,8 @@ public class ConversationItemFooter extends ConstraintLayout {
timerView.stopAnimation();
}
public void setMessageRecord(@NonNull MessageRecord messageRecord, @NonNull Locale locale) {
presentDate(messageRecord, locale);
public void setMessageRecord(@NonNull MessageRecord messageRecord, @NonNull Locale locale, @NonNull ConversationItemDisplayMode displayMode) {
presentDate(messageRecord, locale, displayMode);
presentSimInfo(messageRecord);
presentTimer(messageRecord);
presentInsecureIndicator(messageRecord);
@@ -218,7 +218,7 @@ public class ConversationItemFooter extends ConstraintLayout {
}
}
public TextView getDateView() {
public View getDateView() {
return dateView;
}
@@ -300,7 +300,7 @@ public class ConversationItemFooter extends ConstraintLayout {
return speedToggleHitRect;
}
private void presentDate(@NonNull MessageRecord messageRecord, @NonNull Locale locale) {
private void presentDate(@NonNull MessageRecord messageRecord, @NonNull Locale locale, @NonNull ConversationItemDisplayMode displayMode) {
dateView.forceLayout();
if (messageRecord.isFailed()) {
int errorMsg;
@@ -320,7 +320,11 @@ public class ConversationItemFooter extends ConstraintLayout {
} else if (MessageRecordUtil.isScheduled(messageRecord)) {
dateView.setText(DateUtils.getOnlyTimeString(getContext(), locale, ((MediaMmsMessageRecord) messageRecord).getScheduledDate()));
} else {
dateView.setText(DateUtils.getSimpleRelativeTimeSpanString(getContext(), locale, messageRecord.getTimestamp()));
String date = DateUtils.getSimpleRelativeTimeSpanString(getContext(), locale, messageRecord.getTimestamp());
if (displayMode != ConversationItemDisplayMode.DETAILED && messageRecord instanceof MediaMmsMessageRecord && ((MediaMmsMessageRecord) messageRecord).isEditMessage()) {
date = getContext().getString(R.string.ConversationItem_edited_timestamp_footer, date);
}
dateView.setText(date);
}
}
@@ -360,17 +364,12 @@ public class ConversationItemFooter extends ConstraintLayout {
}
} else if (!messageRecord.isOutgoing() && !messageRecord.isMediaPending()) {
SignalExecutors.BOUNDED.execute(() -> {
ExpiringMessageManager expirationManager = ApplicationDependencies.getExpiringMessageManager();
long id = messageRecord.getId();
boolean mms = messageRecord.isMms();
long id = messageRecord.getId();
boolean mms = messageRecord.isMms();
long now = System.currentTimeMillis();
if (mms) {
SignalDatabase.messages().markExpireStarted(id);
} else {
SignalDatabase.messages().markExpireStarted(id);
}
expirationManager.scheduleDeletion(id, mms, messageRecord.getExpiresIn());
SignalDatabase.messages().markExpireStarted(id, now);
ApplicationDependencies.getExpiringMessageManager().scheduleDeletion(id, mms, now, messageRecord.getExpiresIn());
});
}
} else {

View File

@@ -5,6 +5,7 @@ import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.text.SpannableString;
import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.view.KeyEvent;
@@ -39,9 +40,13 @@ import org.thoughtcrime.securesms.components.emoji.EmojiEventListener;
import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
import org.thoughtcrime.securesms.components.voice.VoiceNotePlaybackState;
import org.thoughtcrime.securesms.conversation.ConversationMessage;
import org.thoughtcrime.securesms.conversation.ConversationStickerSuggestionAdapter;
import org.thoughtcrime.securesms.conversation.VoiceNoteDraftView;
import org.thoughtcrime.securesms.database.DraftTable;
import org.thoughtcrime.securesms.database.model.MessageId;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.Quote;
import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.keyboard.KeyboardPage;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
@@ -53,6 +58,7 @@ import org.thoughtcrime.securesms.mms.QuoteModel;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.MessageRecordUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
@@ -88,6 +94,8 @@ public class InputPanel extends LinearLayout
private View recordingContainer;
private View recordLockCancel;
private ViewGroup composeContainer;
private View editMessageLabel;
private View editMessageCancel;
private MicrophoneRecorderView microphoneRecorderView;
private SlideToCancel slideToCancel;
@@ -105,6 +113,7 @@ public class InputPanel extends LinearLayout
private boolean hideForSelection;
private ConversationStickerSuggestionAdapter stickerSuggestionAdapter;
private MessageRecord messageToEdit;
public InputPanel(Context context) {
super(context);
@@ -144,6 +153,8 @@ public class InputPanel extends LinearLayout
findViewById(R.id.microphone),
TimeUnit.HOURS.toSeconds(1),
() -> microphoneRecorderView.cancelAction(false));
this.editMessageLabel = findViewById(R.id.edit_message);
this.editMessageCancel = findViewById(R.id.input_panel_exit_edit_mode);
this.recordLockCancel.setOnClickListener(v -> microphoneRecorderView.cancelAction(true));
@@ -167,6 +178,8 @@ public class InputPanel extends LinearLayout
stickerSuggestion.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
stickerSuggestion.setAdapter(stickerSuggestionAdapter);
editMessageCancel.setOnClickListener(v -> exitEditMessageMode());
}
public void setListener(final @NonNull Listener listener) {
@@ -183,7 +196,7 @@ public class InputPanel extends LinearLayout
public void setQuote(@NonNull GlideRequests glideRequests,
long id,
@NonNull Recipient author,
@NonNull CharSequence body,
@Nullable CharSequence body,
@NonNull SlideDeck attachments,
@NonNull QuoteModel.Type quoteType)
{
@@ -372,6 +385,52 @@ public class InputPanel extends LinearLayout
quoteView.setWallpaperEnabled(enabled);
}
public void enterEditMessageMode(@NonNull GlideRequests glideRequests, @NonNull ConversationMessage messageToEdit, boolean fromDraft) {
SpannableString textToEdit = messageToEdit.getDisplayBody(getContext());
if (!fromDraft) {
composeText.setText(textToEdit);
composeText.setSelection(textToEdit.length());
}
Quote quote = MessageRecordUtil.getQuote(messageToEdit.getMessageRecord());
if (quote == null) {
clearQuote();
} else {
setQuote(glideRequests, quote.getId(), Recipient.resolved(quote.getAuthor()), quote.getDisplayText(), quote.getAttachment(), quote.getQuoteType());
}
this.messageToEdit = messageToEdit.getMessageRecord();
updateEditModeUi();
}
public void exitEditMessageMode() {
if (messageToEdit != null) {
composeText.setText("");
messageToEdit = null;
quoteView.setMessageType(QuoteView.MessageType.PREVIEW);
}
updateEditModeUi();
}
private void updateEditModeUi() {
if (inEditMessageMode()) {
ViewUtil.focusAndShowKeyboard(composeText);
editMessageLabel.setVisibility(View.VISIBLE);
editMessageCancel.setVisibility(View.VISIBLE);
if (listener != null) {
listener.onEnterEditMode();
}
} else {
editMessageLabel.setVisibility(View.GONE);
editMessageCancel.setVisibility(View.GONE);
if (listener != null) {
listener.onExitEditMode();
}
}
}
public boolean inEditMessageMode() {
return messageToEdit != null;
}
public void setHideForMessageRequestState(boolean hideForMessageRequestState) {
this.hideForMessageRequestState = hideForMessageRequestState;
updateVisibility();
@@ -617,6 +676,16 @@ public class InputPanel extends LinearLayout
}
}
public @Nullable MessageRecord getEditMessage() {
return messageToEdit;
}
public @Nullable MessageId getEditMessageId() {
if (messageToEdit == null) {
return null;
}
return new MessageId(messageToEdit.getId());
}
public interface Listener extends VoiceNoteDraftView.Listener {
void onRecorderStarted();
void onRecorderLocked();
@@ -628,6 +697,8 @@ public class InputPanel extends LinearLayout
void onStickerSuggestionSelected(@NonNull StickerRecord sticker);
void onQuoteChanged(long id, @NonNull RecipientId author);
void onQuoteCleared();
void onEnterEditMode();
void onExitEditMode();
}
private static class SlideToCancel {

View File

@@ -202,6 +202,7 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
params.width = thumbWidth;
thumbnailView.setLayoutParams(params);
dismissView.setVisibility(messageType == MessageType.PREVIEW ? View.VISIBLE : View.GONE);
}
public void setQuote(GlideRequests glideRequests,