diff --git a/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java b/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java index 09fd2f180..41c8e86af 100644 --- a/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/ConversationActivity.java @@ -189,6 +189,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private boolean isDefaultSms = true; private boolean isSecurityInitialized = false; private boolean successfulForwardingAttempt = false; + private boolean isEditing = false; @Override protected void onCreate(Bundle state, boolean ready) { @@ -489,13 +490,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity Log.e(TAG, "cannot set up in-chat-search: ", e); } - if (!dcChat.canSend()) { - MenuItem attachItem = menu.findItem(R.id.menu_add_attachment); - if (attachItem!=null) { - attachItem.setVisible(false); - } - } - super.onPrepareOptionsMenu(menu); return true; } @@ -504,10 +498,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity public boolean onOptionsItemSelected(MenuItem item) { super.onOptionsItemSelected(item); int itemId = item.getItemId(); - if (itemId == R.id.menu_add_attachment) { - handleAddAttachment(); - return true; - } else if (itemId == R.id.menu_leave) { + if (itemId == R.id.menu_leave) { handleLeaveGroup(); return true; } else if (itemId == R.id.menu_archive_chat) { @@ -731,6 +722,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity * @return */ private ListenableFuture initializeDraft() { + isEditing = false; final SettableFuture future = new SettableFuture<>(); DcMsg draft = dcContext.getDraft(chatId); final String sharedText = RelayUtil.getSharedText(this); @@ -1016,6 +1008,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity DcMsg msg = null; Optional quote = inputPanel.getQuote(); Integer recompress = 0; + boolean editing = isEditing; // for a quick ui feedback, we clear the related controls immediately on sending messages. // for drafts, however, we do not change the controls, the activity may be resumed. @@ -1024,6 +1017,18 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity inputPanel.clearQuote(); } + if (editing) { + int msgId = quote.get().getQuotedMsg().getId(); + Util.runOnAnyBackgroundThread(() -> { + if (action == ACTION_SEND_OUT) { + dcContext.sendEditRequest(msgId, body); + } else { + dcContext.setDraft(chatId, null); + } + }); + return future; + } + if(slideDeck!=null) { if (action==ACTION_SEND_OUT) { @@ -1162,7 +1167,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity return; } - if (composeText.getText().length() == 0 && !attachmentManager.isAttachmentPresent()) { + if (!isEditing && composeText.getText().length() == 0 && !attachmentManager.isAttachmentPresent()) { buttonToggle.display(attachButton); quickAttachmentToggle.show(); } else { @@ -1280,9 +1285,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } } + @Override + public void onQuoteDismissed() { + isEditing = false; + composeText.setText(""); + } + // media selected by the system keyboard @Override public void onMediaSelected(@NonNull Uri uri, String contentType) { + if (isEditing) return; if (MediaUtil.isImageType(contentType)) { sendSticker(uri, contentType); } else if (MediaUtil.isVideoType(contentType)) { @@ -1433,6 +1445,25 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity inputPanel.clickOnComposeInput(); } + @Override + public void handleEditMessage(DcMsg msg) { + isEditing = true; + Recipient author = new Recipient(this, dcContext.getContact(msg.getFromId())); + + SlideDeck slideDeck = new SlideDeck(); + String text = msg.getSummarytext(500); + + inputPanel.setQuote(GlideApp.with(this), + msg, + msg.getTimestamp(), + author, + text, + slideDeck); + + setDraftText(msg.getText()); + inputPanel.clickOnComposeInput(); + } + @Override public void onAttachmentChanged() { handleSecurityChange(isSecureText, isDefaultSms); diff --git a/src/main/java/org/thoughtcrime/securesms/ConversationFragment.java b/src/main/java/org/thoughtcrime/securesms/ConversationFragment.java index 01409ba96..f00c7aeb6 100644 --- a/src/main/java/org/thoughtcrime/securesms/ConversationFragment.java +++ b/src/main/java/org/thoughtcrime/securesms/ConversationFragment.java @@ -324,6 +324,7 @@ public class ConversationFragment extends MessageSelectorFragment menu.findItem(R.id.menu_context_details).setVisible(false); menu.findItem(R.id.menu_context_share).setVisible(false); menu.findItem(R.id.menu_context_reply).setVisible(false); + menu.findItem(R.id.menu_context_edit).setVisible(false); menu.findItem(R.id.menu_context_reply_privately).setVisible(false); menu.findItem(R.id.menu_add_to_home_screen).setVisible(false); } else { @@ -333,6 +334,8 @@ public class ConversationFragment extends MessageSelectorFragment menu.findItem(R.id.menu_context_share).setVisible(messageRecord.hasFile()); boolean canReply = canReplyToMsg(messageRecord); menu.findItem(R.id.menu_context_reply).setVisible(chat.canSend() && canReply); + boolean canEdit = canEditMsg(messageRecord); + menu.findItem(R.id.menu_context_edit).setVisible(chat.canSend() && canEdit); boolean showReplyPrivately = chat.isMultiUser() && !messageRecord.isOutgoing() && canReply; menu.findItem(R.id.menu_context_reply_privately).setVisible(showReplyPrivately); menu.findItem(R.id.menu_add_to_home_screen).setVisible(messageRecord.getType() == DcMsg.DC_MSG_WEBXDC); @@ -361,6 +364,10 @@ public class ConversationFragment extends MessageSelectorFragment return !dcMsg.isInfo() && dcMsg.getType() != DcMsg.DC_MSG_VIDEOCHAT_INVITATION; } + static boolean canEditMsg(DcMsg dcMsg) { + return dcMsg.isOutgoing() && !dcMsg.isInfo() && dcMsg.getType() == DcMsg.DC_MSG_TEXT; + } + public void handleClearChat() { handleDeleteMessages((int) chatId, getListAdapter().getMessageIds()); } @@ -461,6 +468,16 @@ public class ConversationFragment extends MessageSelectorFragment listener.handleReplyMessage(message); } + @SuppressLint("RestrictedApi") + private void handleEditMessage(final DcMsg message) { + if (getActivity() != null) { + //noinspection ConstantConditions + ((AppCompatActivity) getActivity()).getSupportActionBar().collapseActionView(); + } + + listener.handleEditMessage(message); + } + private void handleReplyMessagePrivately(final DcMsg msg) { if (getActivity() != null) { @@ -591,6 +608,7 @@ public class ConversationFragment extends MessageSelectorFragment public interface ConversationFragmentListener { void handleReplyMessage(DcMsg messageRecord); + void handleEditMessage(DcMsg messageRecord); } private class ConversationScrollListener extends OnScrollListener { @@ -958,6 +976,10 @@ public class ConversationFragment extends MessageSelectorFragment handleReplyMessage(getSelectedMessageRecord(getListAdapter().getSelectedItems())); actionMode.finish(); return true; + } else if (itemId == R.id.menu_context_edit) { + handleEditMessage(getSelectedMessageRecord(getListAdapter().getSelectedItems())); + actionMode.finish(); + return true; } else if (itemId == R.id.menu_context_reply_privately) { handleReplyMessagePrivately(getSelectedMessageRecord(getListAdapter().getSelectedItems())); return true; diff --git a/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java b/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java index c8c0c22b9..c307c55dd 100644 --- a/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java +++ b/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java @@ -141,6 +141,7 @@ public class InputPanel extends ConstraintLayout public void clearQuoteWithoutAnimation() { quoteView.dismiss(); + if (listener != null) listener.onQuoteDismissed(); } public void clearQuote() { @@ -152,6 +153,7 @@ public class InputPanel extends ConstraintLayout @Override public void onAnimationEnd(@NonNull Animator animation) { quoteView.dismiss(); + if (listener != null) listener.onQuoteDismissed(); } }); @@ -329,6 +331,7 @@ public class InputPanel extends ConstraintLayout void onRecorderCanceled(); void onRecorderPermissionRequired(); void onEmojiToggle(); + void onQuoteDismissed(); } private static class SlideToCancel { diff --git a/src/main/res/menu/conversation.xml b/src/main/res/menu/conversation.xml index 46be4a3a6..3a036cf23 100644 --- a/src/main/res/menu/conversation.xml +++ b/src/main/res/menu/conversation.xml @@ -41,9 +41,6 @@ - - diff --git a/src/main/res/menu/conversation_context.xml b/src/main/res/menu/conversation_context.xml index 7be2b04a7..4aa6d5ece 100644 --- a/src/main/res/menu/conversation_context.xml +++ b/src/main/res/menu/conversation_context.xml @@ -44,6 +44,10 @@ android:id="@+id/menu_context_reply_privately" app:showAsAction="never" /> + +