From 7ae4d078e2eb9bf4b60da1acdc431e0d00abc96e Mon Sep 17 00:00:00 2001 From: rapterjet2004 Date: Thu, 17 Sep 2026 11:48:25 -0500 Subject: [PATCH] fix: Add Ctrl+Enter shortcut to send messages in Nextcloud Talk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MessageInputFragment.kt: added a setOnKeyListener on the message input EditText that detects Ctrl+Enter (or Ctrl+Numpad-Enter) on key-down, and dispatches to whichever action button is currently active (edit-confirm, thread-submit, or normal send) via performClick() — reusing existing send/edit/thread logic exactly. Plain Enter is left unconsumed, so newline insertion is unaffected. Assisted-by: Claude Code:claude-sonnet-5 Signed-off-by: rapterjet2004 --- .../talk/chat/MessageInputFragment.kt | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/app/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.kt b/app/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.kt index 5827bb6471e..06b6f9bfc3c 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.kt @@ -20,6 +20,7 @@ import android.text.TextWatcher import android.util.Log import android.util.TypedValue import android.view.Gravity +import android.view.KeyEvent import android.view.LayoutInflater import android.view.MenuItem import android.view.MotionEvent @@ -113,6 +114,8 @@ class MessageInputFragment : Fragment() { @Inject lateinit var dateUtils: DateUtils + private enum class KeyboardSendTarget { NONE, EDIT, THREAD, SEND } + private val messageInputViewModel: MessageInputViewModel by activityViewModels() lateinit var binding: FragmentMessageInputBinding private lateinit var conversationInternalId: String @@ -511,6 +514,33 @@ class MessageInputFragment : Fragment() { submitMessage(false) } + binding.fragmentMessageInputView.inputEditText.setOnKeyListener { _, keyCode, event -> + val target = resolveKeyboardSendTarget( + keyCode = keyCode, + action = event.action, + isCtrlPressed = event.isCtrlPressed, + isEditButtonVisible = binding.fragmentEditView.editMessageView.isVisible, + isThreadButtonVisible = binding.fragmentMessageInputView.submitThreadButton.isVisible, + isThreadButtonEnabled = binding.fragmentMessageInputView.submitThreadButton.isEnabled, + isSendButtonVisible = binding.fragmentMessageInputView.messageSendButton.isVisible + ) + when (target) { + KeyboardSendTarget.EDIT -> { + binding.fragmentMessageInputView.editMessageButton.performClick() + true + } + KeyboardSendTarget.THREAD -> { + binding.fragmentMessageInputView.submitThreadButton.performClick() + true + } + KeyboardSendTarget.SEND -> { + binding.fragmentMessageInputView.button.performClick() + true + } + KeyboardSendTarget.NONE -> false + } + } + binding.fragmentMessageInputView.editMessageButton.setOnClickListener { val editable = binding.fragmentMessageInputView.inputEditText!!.editableText replaceMentionChipSpans(editable) @@ -1267,6 +1297,31 @@ class MessageInputFragment : Fragment() { return isChannel && permissions?.hasChatPermission() == false && permissions.hasReactPermission() == true } + private fun resolveKeyboardSendTarget( + keyCode: Int, + action: Int, + isCtrlPressed: Boolean, + isEditButtonVisible: Boolean, + isThreadButtonVisible: Boolean, + isThreadButtonEnabled: Boolean, + isSendButtonVisible: Boolean + ): KeyboardSendTarget { + val isSendChord = action == KeyEvent.ACTION_DOWN && + isCtrlPressed && + (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER) + + if (!isSendChord) { + return KeyboardSendTarget.NONE + } + + return when { + isEditButtonVisible -> KeyboardSendTarget.EDIT + isThreadButtonVisible && isThreadButtonEnabled -> KeyboardSendTarget.THREAD + isSendButtonVisible -> KeyboardSendTarget.SEND + else -> KeyboardSendTarget.NONE + } + } + companion object { fun newInstance() = MessageInputFragment() private val TAG: String = MessageInputFragment::class.java.simpleName