Repository navigation
feat(chat): record video messages from the message input - #6818
ToteMeiSter wants to merge 23 commits into
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Priority: ➖ Normal Merge Risk: 🔵 Low · up to
|
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Organization UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
b493e8b5-ae18-41b3-a265-55028ef6b613
📒 Files selected for processing (27)
app/build.gradle.ktsapp/src/main/java/com/nextcloud/talk/chat/CameraLens.ktapp/src/main/java/com/nextcloud/talk/chat/ChatActivity.ktapp/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.ktapp/src/main/java/com/nextcloud/talk/chat/MessageInputVoiceRecordingFragment.ktapp/src/main/java/com/nextcloud/talk/chat/RecordButtonGesture.ktapp/src/main/java/com/nextcloud/talk/chat/RecordHintPopup.ktapp/src/main/java/com/nextcloud/talk/chat/RecordInputMode.ktapp/src/main/java/com/nextcloud/talk/chat/RecordingStopCoordinator.ktapp/src/main/java/com/nextcloud/talk/chat/VideoMessageRecorder.ktapp/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.ktapp/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.javaapp/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferencesImpl.ktapp/src/main/res/drawable/bg_record_hint.xmlapp/src/main/res/drawable/ic_record_hint_arrow.xmlapp/src/main/res/layout/activity_chat.xmlapp/src/main/res/layout/view_message_input.xmlapp/src/main/res/layout/view_record_hint.xmlapp/src/main/res/values/dimens.xmlapp/src/main/res/values/strings.xmlapp/src/test/java/com/nextcloud/talk/chat/RecordButtonGestureTest.ktapp/src/test/java/com/nextcloud/talk/chat/RecordHintPopupPlacementTest.ktapp/src/test/java/com/nextcloud/talk/chat/RecordingStopCoordinatorTest.ktapp/src/test/java/com/nextcloud/talk/chat/ShouldVibrateOnRecordingChangeTest.ktapp/src/test/java/com/nextcloud/talk/chat/VideoMessageRecordingTest.ktapp/src/test/java/com/nextcloud/talk/chat/VideoRecorderLifecycleTest.ktapp/src/test/java/com/nextcloud/talk/chat/VideoRecordingRecreationTest.kt
Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 1 remain after this review.
|
Hi @ToteMeiSter thank you for your contributions. |
There was a problem hiding this comment.
Actionable comments posted: 2
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Organization UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
1db88cfb-cd9e-4493-9d4e-d139a302b51f
📒 Files selected for processing (16)
app/src/main/java/com/nextcloud/talk/chat/ChatActivity.ktapp/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.ktapp/src/main/java/com/nextcloud/talk/chat/MessageInputVoiceRecordingFragment.ktapp/src/main/java/com/nextcloud/talk/chat/RecordButtonGesture.ktapp/src/main/java/com/nextcloud/talk/chat/VideoMessageRecorder.ktapp/src/main/java/com/nextcloud/talk/chat/VideoPreviewPlacement.ktapp/src/main/res/drawable/video_recording_dot.xmlapp/src/main/res/layout/activity_chat.xmlapp/src/main/res/layout/fragment_message_input_voice_recording.xmlapp/src/main/res/values/colors.xmlapp/src/main/res/values/dimens.xmlapp/src/test/java/com/nextcloud/talk/chat/RecordButtonGestureTest.ktapp/src/test/java/com/nextcloud/talk/chat/VideoFrameAspectTest.ktapp/src/test/java/com/nextcloud/talk/chat/VideoPreviewPlacementCompactPanelTest.ktapp/src/test/java/com/nextcloud/talk/chat/VideoPreviewPlacementTest.ktapp/src/test/java/com/nextcloud/talk/chat/VideoRecordingRecreationTest.kt
🚧 Files skipped from review as they are similar to previous changes (2)
- app/src/test/java/com/nextcloud/talk/chat/RecordButtonGestureTest.kt
- app/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.kt
Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.
|
Update: four commits were added on top of the original change. They refine the video message UI of this PR and were tested on a device (Huawei DEL-LX9) with a fork build:
They stay in this PR instead of a separate one because they change the same recording UI that this PR introduces; a follow-up PR on top of an unmerged one would need the same code reviewed twice. The CodeRabbit findings are fixed in 9447da7, 18b8f2a and 59de428 (answers in the threads). All commits are carried up the stack into #6819 and #6820. This comment was prepared with the help of an AI assistant (Claude Code) and reviewed before posting. |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
🟡 Changes recommended
Recording finalization and cleanup can lose accepted sends, disrupt subsequent recordings, or deliver results before the chat is ready.
6 open findings
Queue recording results until chat and capabilities are ready · New Prevent new recordings until previous video finalization completes · New Preserve accepted video sends after chat ViewModel destruction · New Support accessible mode switching via click actions · New Allow video recording without media-library read permission · New Release video audio focus during ViewModel cleanup · New
What changed in this PR
Adds inline video messages to Nextcloud Talk’s chat input, using the existing recording controls and attachment upload flow.
Changes:
- Adds persistent voice/video mode selection and hold-to-record gestures.
- Introduces CameraX recording with live preview, camera switching, and rotation handling.
- Includes voice-recording lifecycle fixes, UI resources, and recording helper tests.
| File | Description |
|---|---|
| app/src/test/java/com/nextcloud/talk/chat/VideoRecordingRecreationTest.kt | Tests recording restoration and outcomes. |
| app/src/test/java/com/nextcloud/talk/chat/VideoRecorderLifecycleTest.kt | Tests recorder-owned lifecycle. |
| app/src/test/java/com/nextcloud/talk/chat/VideoPreviewPlacementTest.kt | Tests preview sizing. |
| app/src/test/java/com/nextcloud/talk/chat/VideoPreviewPlacementCompactPanelTest.kt | Tests compact-panel preview sizing. |
| app/src/test/java/com/nextcloud/talk/chat/VideoMessageRecordingTest.kt | Tests modes and recording outcomes. |
| app/src/test/java/com/nextcloud/talk/chat/VideoFrameAspectTest.kt | Tests frame orientation calculations. |
| app/src/test/java/com/nextcloud/talk/chat/ShouldVibrateOnRecordingChangeTest.kt | Tests vibration filtering. |
| app/src/test/java/com/nextcloud/talk/chat/RecordingStopCoordinatorTest.kt | Tests delayed stop coordination. |
| app/src/test/java/com/nextcloud/talk/chat/RecordHintPopupPlacementTest.kt | Tests hint positioning. |
| app/src/test/java/com/nextcloud/talk/chat/RecordButtonGestureTest.kt | Tests tap and hold classification. |
| app/src/main/res/values/strings.xml | Adds recording labels and hints. |
| app/src/main/res/values/dimens.xml | Defines preview dimensions. |
| app/src/main/res/values/colors.xml | Adds recording overlay color. |
| app/src/main/res/layout/view_record_hint.xml | Defines hint popup layout. |
| app/src/main/res/layout/view_message_input.xml | Adds record-button tint. |
| app/src/main/res/layout/fragment_message_input_voice_recording.xml | Adds video progress indicator. |
| app/src/main/res/layout/activity_chat.xml | Adds preview overlay and camera switch. |
| app/src/main/res/drawable/video_recording_dot.xml | Adds recording indicator drawable. |
| app/src/main/res/drawable/ic_record_hint_arrow.xml | Adds hint arrow. |
| app/src/main/res/drawable/bg_record_hint.xml | Adds hint background. |
| app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferencesImpl.kt | Persists recording mode. |
| app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java | Exposes recording-mode preference. |
| app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt | Owns video recorder and shared state. |
| app/src/main/java/com/nextcloud/talk/chat/VideoPreviewPlacement.kt | Calculates preview geometry. |
| app/src/main/java/com/nextcloud/talk/chat/VideoMessageRecorder.kt | Implements CameraX recording lifecycle. |
| app/src/main/java/com/nextcloud/talk/chat/RecordInputMode.kt | Defines voice and video modes. |
| app/src/main/java/com/nextcloud/talk/chat/RecordingStopCoordinator.kt | Coordinates stops during camera switching. |
| app/src/main/java/com/nextcloud/talk/chat/RecordHintPopup.kt | Displays anchored recording hints. |
| app/src/main/java/com/nextcloud/talk/chat/RecordButtonGesture.kt | Classifies recording gestures. |
| app/src/main/java/com/nextcloud/talk/chat/MessageInputVoiceRecordingFragment.kt | Shares locked controls with video. |
| app/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.kt | Integrates mode switching and hold recording. |
| app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt | Integrates preview, permissions, restoration, and uploads. |
| app/src/main/java/com/nextcloud/talk/chat/CameraLens.kt | Adds camera selection and binding helpers. |
| app/build.gradle.kts | Adds CameraX dependencies. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Organization UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
6a554354-5aa3-4e60-943f-416c55e3d0b9
📒 Files selected for processing: 7
app/src/main/java/com/nextcloud/talk/chat/ChatActivity.ktapp/src/main/java/com/nextcloud/talk/chat/MessageInputFragment.ktapp/src/main/java/com/nextcloud/talk/chat/RecordInputMode.ktapp/src/main/java/com/nextcloud/talk/chat/VideoMessageRecorder.ktapp/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.ktapp/src/test/java/com/nextcloud/talk/chat/RecordStartTest.ktapp/src/test/java/com/nextcloud/talk/chat/VideoRecordingCopilotFixesTest.kt
Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.
A short tap on the record button switches between voice and video mode. The mode is stored in AppPreferences and shown by the button icon and a snackbar hint. The result is a regular video attachment. In video mode holding the button records with CameraX (front camera, 720p with fallback, about 2.5 Mbit/s, at most 120 s, then it stops and is sent). Sliding left cancels, sliding up locks. The gestures, the lock state, the timer and the locked recording view are shared with the voice recording. The preview is shown above the input and the camera can be switched during the recording. An unfinished recording is cancelled and the camera released when the chat is paused. Too short recordings show a hint, camera errors a message. The finished mp4 goes through the regular file upload. Refs nextcloud#6812 Signed-off-by: Krainov Gleb <[email protected]> Assisted-by: Claude-Code:claude-opus-5-5
… popup The record button used to start a recording on the first touch, so a short tap that should switch the mode started a recording first. Recording now starts only when the button is held. A tap shows the record hint as a popup above the button instead of a snackbar, placed in the window of the button and kept above it after layout changes. Signed-off-by: Krainov Gleb <[email protected]> Assisted-by: Claude-Code:claude-opus-5-5
The activity is recreated on rotation while the video recorder belonged to it: onPause cancelled the recording and deleted its file, and binding the camera to the next activity made the CameraX recorder configure itself in a state where that is not allowed. - The video recorder lives in ChatViewModel like the voice recorder. The activity attaches its preview and callback, and the recorder lets go of them when the activity is destroyed. A result that arrives without an activity is delivered to the next one. - The camera is bound to a lifecycle of the recorder itself, not to the one of the activity, so it stays bound while the activity is recreated. - A new activity picks up the recording and locks it when it was held, so the timer, stop, send and cancel are shown. - The recording is cancelled when the user leaves the chat, not on a configuration change. - A recording cut off by the camera is not lost but shown in the attachment preview. - The video is recorded in the rotation of the sensor. - A recording is never stopped while a switched camera settles. Signed-off-by: Krainov Gleb <[email protected]> Assisted-by: Claude-Code:claude-opus-5-5
The container of the video recording preview did not consume touches, so taps on it reached the chat below. Make it consume them and hide it from TalkBack, where it is only decoration. Signed-off-by: Krainov Gleb <[email protected]> Assisted-by: Claude-Code:claude-opus-5-5
…g start The start time was taken on ACTION_DOWN, but the recording starts only after the 400-600 ms hold threshold, so a voice message was judged too short (or long enough) by a duration which included the hold. Take the time in beginRecording() after the recording has started and use the monotonic clock. Signed-off-by: Krainov Gleb <[email protected]> Assisted-by: Claude-Code:claude-opus-5-5
…alized it Send or delete of a locked video recording cleared the lock and the in-progress state at once, while CameraX was still finalizing the file. A new recording started in that window was silently dropped in beginRecording(). For video the state is now released in onVideoRecordingFinished(), which CameraX reaches for every outcome (send, cancel, error, too short). Audio, and a video recorder which is already idle, are still cleared at once. Signed-off-by: Krainov Gleb <[email protected]> Assisted-by: Claude-Code:claude-opus-5-5
…rded frame The locked video recording preview was a fixed 180x240dp box floating over the message bubbles, squashed to about 2:1 in landscape. Now a scrim dims the chat pane (it blocks touches and is hidden from TalkBack), and the preview is centered in it in the aspect of the recorded frame, fitted with margins, at most 75% of the width and 480dp on the longest side. The placement is a pure function (videoPreviewPlacement) with unit tests; it is recomputed when the pane changes size and when the recorder learns the frame aspect from CameraX. Signed-off-by: Krainov Gleb <[email protected]> Assisted-by: Claude-Code:claude-opus-5-5
…entation, hide chat from TalkBack - Center by Gravity.CENTER and change only width and height: the margin comparison never settled in RTL and re-laid out the chat every frame. - The frame aspect is turned into screen coordinates by the difference of the recording and the display rotation (screenFrameAspect), so the frame matches the picture with auto-rotate off and after a turn during the recording. - While recording, the siblings of the scrim are hidden from TalkBack and get their previous values back afterwards. - The preview size is recalculated after a camera switch. Signed-off-by: Krainov Gleb <[email protected]> Assisted-by: Claude-Code:claude-opus-5-5
In video mode the locked recording panel is one row: delete, red dot in a progress ring of the 120 s limit, timer, send. The voice panel is unchanged. The preview keeps a smaller gap on a short area. Signed-off-by: Krainov Gleb <[email protected]> Assisted-by: Claude-Code:claude-opus-5-5
- Hide the whole recording indicator from TalkBack, so the progress ring does not announce its percentage every second. - Relative padding of the timer, so the gap stays between dot and timer in RTL. - No inset around the progress ring, so it fills its 24dp. - The ring scale is set in code only (PROGRESS_MAX). Signed-off-by: Krainov Gleb <[email protected]> Assisted-by: Claude-Code:claude-opus-5-5
A video recording result that waited for a new activity was delivered from onCreate, before currentConversation and spreedCapabilities were set: the send path and the preview path could throw. The pending result is now delivered once the initial capabilities state has initialized both; a running recording still reattaches at once. The delete and send buttons of the compact video row announced the voice recording labels to TalkBack. Signed-off-by: Krainov Gleb <[email protected]> Assisted-by: Claude-Code:claude-opus-5-5
- A recording result that arrives before the chat of a recreated activity is loaded is held by the recorder and delivered once it is loaded, as the results that were already pending. - No recording of either kind starts while a video is still being finalized. Its end would clear the state and the audio focus of the new recording. - A video that was sent is finalized and uploaded even if the chat is left before CameraX has finished; clearing the view model gives the audio focus of a video recording back. - The record button switches the mode on a click as well (TalkBack, Switch Access, keyboard); a touch does not switch it twice. - Recording a video no longer asks for access to the media files, it writes to the app cache. Assisted-by: Claude-Code:claude-sonnet-5-5 Signed-off-by: Krainov Gleb <[email protected]>
…ding After a recreation of the activity the lock observer shows the locked recording panel at once, but the capabilities arrive later and their handler replaced the panel with the ordinary message input. The lock stayed set, so nothing brought the panel back. The handler now keeps the panel of a locked recording. Assisted-by: Claude-Code:claude-sonnet-5-5 Signed-off-by: Krainov Gleb <[email protected]>
The photo of the system camera is written to the app cache (createAttachmentFile) and handed over through the own file provider, so it needs no access to the media. Drop the gate in onPickCameraResult and the permission callback branch that only resumed that preview; the callback is still used by the voice recording. Assisted-by: Claude-Code:claude-sonnet-5-5 Signed-off-by: Krainov Gleb <[email protected]>
View.onTouchEvent posts the click of a released touch, so it ran after the touch router had reset its flag and switched the recording mode once more, e.g. right after a recording was sent. The release is now consumed by the touch listener and never reaches the view; the pressed state is cleared by hand. The click listener stays for TalkBack, Switch Access and the keyboard. Replaces RecordButtonClickRouter. Assisted-by: Claude-Code:claude-sonnet-5-5 Signed-off-by: Krainov Gleb <[email protected]>
70c586b to
4fddff2
Compare
…essage records sound The recording asked for the audio focus and opened the microphone in the same call. The player of another app pauses some time after it lost the focus, so the first moments of a video message contained the end of the music from the speaker. The camera and the preview are bound at once, but the recording starts only when AudioManager.isMusicActive is false, for at most one second; without music there is no delay. Stopping in the meantime ends the start as before. MusicSilenceGate is the same file as in the voice message fix. Assisted-by: Claude-Code:claude-sonnet-5-5 Signed-off-by: Krainov Gleb <[email protected]>
|
Hello there, We hope that the review process is going smooth and is helpful for you. We want to ensure your pull request is reviewed to your satisfaction. If you have a moment, our community management team would very much appreciate your feedback on your experience with this PR review process. Your feedback is valuable to us as we continuously strive to improve our community developer experience. Please take a moment to complete our short survey by clicking on the following link: https://cloud.nextcloud.com/apps/forms/s/i9Ago4EQRZ7TWxjfmeEpPkf6 Thank you for contributing to Nextcloud and we hope to hear from you soon! (If you believe you should not receive this message, you can add yourself to the blocklist.) |
|
Thank you for you contributions. We will try to review this soon! |
1 similar comment
|
Thank you for you contributions. We will try to review this soon! |
…aster Conflicts in ChatActivity.kt: master moved the playback controller out of the activity (voice message rework) and joins the room through ensureJoined()/sendSharedFiles(). Kept from this PR: orientation tracker, cancel of an active video recording in onStop, and attaching a pending video result after the conversation is loaded. Dropped: the AudioUtils lifecycle observer and the MediaMetadataRetriever import, which master no longer uses in the activity. Assisted-by: Claude-Code:claude-sonnet-5-5 Signed-off-by: Krainov Gleb <[email protected]>
…aster Second master merge (master 20c17d0). Conflict in values/strings.xml (nextcloud#6820, nextcloud#6823): kept the PR strings; nc_remove_file stays dropped, the PR ThumbnailStrip no longer uses it and master removes unused strings. Restored drawable ic_baseline_flip_camera_android_24: master removed it in 19d5e7f as unused, but activity_chat.xml (all four PRs) and PhotoCaptureScreen.kt (nextcloud#6820, nextcloud#6823) still reference it. Assisted-by: Claude-Code:claude-sonnet-5-5 Signed-off-by: Krainov Gleb <[email protected]>
Remove the empty super.onCleared call of the chat view model, inflate the record hint with a parent view root and anchor it by Gravity.START (the x offset is mirrored in a right-to-left window), and create the lifecycle of the recording camera with the public LifecycleRegistry constructor instead of the test-only createUnsafe; its test runs under Robolectric for the main thread. Assisted-by: Claude-Code:claude-sonnet-5-5 Signed-off-by: Krainov Gleb <[email protected]>
The hint root is a FrameLayout with the arrow aligned to the bottom, and the video preview container is a sibling of the dimming view instead of its only child. Geometry, outline clipping and arrow offset stay the same. Assisted-by: Claude-Code:claude-sonnet-5-5 Signed-off-by: Krainov Gleb <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: engineering-git-workflow-master (cc3/claude-sonnet-5-5) <[email protected]> Signed-off-by: Krainov Gleb <[email protected]>
d305843 to
67fcbaf
Compare


Refs #6812
This is a feature PR; the issue is not approved yet, the PR is opened for discussion.
🖼️ Screenshots
Feature
A short tap on the record button in the message input switches between voice and video mode (the mode is stored in the preferences and shown by the icon and a hint popup). In video mode:
ChatViewModellike the voice recorder and the camera is bound to a lifecycle of the recorder, not to the activity;Structure
The PR is a stack. It contains the two voice recording fixes as the first commits:
After they are merged only the four commits of the feature remain:
feat(chat): record video messages from the message inputfix(chat): start recording only on hold and show the record hint as a popupfix(chat): keep a video recording over a screen rotationfix(chat): block taps and TalkBack focus on the video recording previewHow to check on a device
Tested by the author on a Huawei DEL-LX9 with a fork build that has this feature together with other fork changes; this branch itself was built and unit-tested.
🚧 TODO
🏁 Checklist
/backport to stable-xx.x🤖 AI (if applicable)
The change was written with the help of Claude Code (Anthropic). The author reviewed the code, built it and ran the unit tests, and checked the behavior on a device with the fork build.