mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-26 19:56:02 +01:00
Add message editing feature.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user