Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
79f9aca
fix(chat): do not vibrate when the voice recording is only shown again
ToteMeiSter Oct 4, 2026
95e7c85
fix(chat): keep a voice recording running over a screen rotation
ToteMeiSter Oct 4, 2026
1ccf96d
feat(chat): record video messages from the message input
ToteMeiSter Oct 4, 2026
6ad6592
fix(chat): start recording only on hold and show the record hint as a…
ToteMeiSter Oct 4, 2026
fcd0d38
fix(chat): keep a video recording over a screen rotation
ToteMeiSter Oct 4, 2026
4528d9e
fix(chat): block taps and TalkBack focus on the video recording preview
ToteMeiSter Oct 4, 2026
62340d7
feat(chat): replace attachment menu with a media bottom sheet
ToteMeiSter Oct 4, 2026
62f51a2
fix(chat): count partial media access as files access on Android 14
ToteMeiSter Oct 4, 2026
1d06ca6
fix(chat): measure the minimum voice message length from the recordin…
ToteMeiSter Oct 5, 2026
c85b647
fix(chat): release a locked video recording only when CameraX has fin…
ToteMeiSter Oct 5, 2026
8bf23e8
fix(chat): dim the chat and fit the video message preview to the reco…
ToteMeiSter Oct 5, 2026
e499e42
fix(chat): center the video preview by gravity, follow the screen ori…
ToteMeiSter Oct 5, 2026
bf006f8
fix(chat): one-line recording panel for video messages, larger preview
ToteMeiSter Oct 5, 2026
fa2818c
fix(chat): video recording row review fixes
ToteMeiSter Oct 5, 2026
f3fdd7c
fix(chat): ask for the selected-photos permission and judge the resul…
ToteMeiSter Oct 5, 2026
2163da2
fix(chat): defer video result until chat is loaded, video a11y labels
ToteMeiSter Oct 5, 2026
b8eaef7
fix(attachment-sheet): offer "select more" for mixed media grants on …
ToteMeiSter Oct 5, 2026
f9b4751
fix(attachment-sheet): re-read media grants when the permission reque…
ToteMeiSter Oct 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ val androidxTestVersion = "1.5.0"
val media3Version = "1.11.0"
val coroutinesVersion = "1.11.0"
val mockitoKotlinVersion = "6.3.0"
val cameraXVersion = "1.6.1"

android {
compileSdk = 37
Expand Down Expand Up @@ -244,6 +245,12 @@ dependencies {
implementation("androidx.lifecycle:lifecycle-common:$lifecycleVersion")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycleVersion")

implementation("androidx.camera:camera-core:$cameraXVersion")
implementation("androidx.camera:camera-camera2:$cameraXVersion")
implementation("androidx.camera:camera-lifecycle:$cameraXVersion")
implementation("androidx.camera:camera-video:$cameraXVersion")
implementation("androidx.camera:camera-view:$cameraXVersion")

implementation("androidx.biometric:biometric:1.1.0")

implementation("io.reactivex.rxjava2:rxandroid:2.1.1")
Expand Down
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

<uses-permission android:name="android.permission.WAKE_LOCK" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.nextcloud.talk.attachmentsheet

/**
* Entries of the attachment sheet that open another screen or flow. The declaration order is the display order.
*/
enum class AttachmentAction {
PICTURE_FROM_CAM,
VIDEO_FROM_CAM,
GALLERY,
FILE_FROM_LOCAL,
FILE_FROM_CLOUD,
CREATE_THREAD,
CREATE_POLL,
SHARE_LOCATION,
SHARE_CONTACT
}

data class AttachmentVisibilityInput(
val isRemoteConversation: Boolean,
val hasGeoLocationCapability: Boolean,
val hasPollsCapability: Boolean,
val isOneToOneConversation: Boolean,
val hasThreadsCapability: Boolean,
val isInsideThread: Boolean,
val hasCamera: Boolean
)

/**
* Decides which entries are offered. Federated conversations only allow the gallery, polls and threads; the video
* entry additionally needs camera hardware.
*/
fun resolveAttachmentActions(input: AttachmentVisibilityInput): List<AttachmentAction> =
AttachmentAction.entries.filter { action ->
when (action) {
AttachmentAction.PICTURE_FROM_CAM,
AttachmentAction.FILE_FROM_LOCAL,
AttachmentAction.FILE_FROM_CLOUD,
AttachmentAction.SHARE_CONTACT -> !input.isRemoteConversation

AttachmentAction.VIDEO_FROM_CAM -> !input.isRemoteConversation && input.hasCamera

AttachmentAction.GALLERY -> true

AttachmentAction.SHARE_LOCATION -> !input.isRemoteConversation && input.hasGeoLocationCapability

AttachmentAction.CREATE_POLL -> input.hasPollsCapability && !input.isOneToOneConversation

AttachmentAction.CREATE_THREAD -> input.hasThreadsCapability && !input.isInsideThread
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.nextcloud.talk.attachmentsheet

import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import com.nextcloud.talk.R

@DrawableRes
internal fun AttachmentAction.iconRes(): Int =
when (this) {
AttachmentAction.PICTURE_FROM_CAM -> R.drawable.ic_baseline_photo_camera_24
AttachmentAction.VIDEO_FROM_CAM -> R.drawable.ic_baseline_videocam_24
AttachmentAction.GALLERY -> R.drawable.baseline_photo_library_24
AttachmentAction.FILE_FROM_LOCAL -> R.drawable.upload
AttachmentAction.FILE_FROM_CLOUD -> R.drawable.ic_share_variant
AttachmentAction.CREATE_THREAD -> R.drawable.outline_forum_24
AttachmentAction.CREATE_POLL -> R.drawable.ic_baseline_bar_chart_24
AttachmentAction.SHARE_LOCATION -> R.drawable.ic_baseline_location_on_24
AttachmentAction.SHARE_CONTACT -> R.drawable.ic_baseline_person_24
}

/**
* Label for the entry, or null when it needs a runtime argument (the cloud entry carries the server name).
*/
@StringRes
internal fun AttachmentAction.labelRes(): Int? =
when (this) {
AttachmentAction.PICTURE_FROM_CAM -> R.string.nc_upload_picture_from_cam
AttachmentAction.VIDEO_FROM_CAM -> R.string.nc_upload_video_from_cam
AttachmentAction.GALLERY -> R.string.nc_gallery
AttachmentAction.FILE_FROM_LOCAL -> R.string.nc_upload_from_device
AttachmentAction.FILE_FROM_CLOUD -> null
AttachmentAction.CREATE_THREAD -> R.string.start_thread
AttachmentAction.CREATE_POLL -> R.string.nc_create_poll
AttachmentAction.SHARE_LOCATION -> R.string.nc_share_location
AttachmentAction.SHARE_CONTACT -> R.string.nc_share_contact
}
Loading