diff --git a/changelog.d/7393.wip b/changelog.d/7393.wip new file mode 100644 index 0000000000..7d82dc5769 --- /dev/null +++ b/changelog.d/7393.wip @@ -0,0 +1 @@ +[Voice Broadcast] Enable the feature (behind a lab flag and only for Android 10 and up) diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index 74ec175d17..fac36ffa52 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -3346,6 +3346,8 @@ Have greater visibility and control over all your sessions. Enable client info recording Record the client name, version, and url to recognise sessions more easily in session manager. + Enable voice broadcast (under active development) + Be able to record and send voice broadcast in room timeline. %s\nis looking a little empty. diff --git a/vector-config/src/main/res/values/config-settings.xml b/vector-config/src/main/res/values/config-settings.xml index 7b7aac8156..504c587b8d 100755 --- a/vector-config/src/main/res/values/config-settings.xml +++ b/vector-config/src/main/res/values/config-settings.xml @@ -49,6 +49,8 @@ false true false + true + false diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt index 255ac6d188..95cf272abd 100644 --- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt +++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt @@ -62,5 +62,5 @@ class DefaultVectorFeatures : VectorFeatures { override fun isQrCodeLoginEnabled(): Boolean = true override fun isQrCodeLoginForAllServers(): Boolean = false override fun isReciprocateQrCodeLogin(): Boolean = false - override fun isVoiceBroadcastEnabled(): Boolean = false + override fun isVoiceBroadcastEnabled(): Boolean = true } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt index 59f9737542..55ec922a57 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt @@ -310,7 +310,7 @@ class MessageComposerFragment : VectorBaseFragment(), A ) attachmentTypeSelector.setAttachmentVisibility( AttachmentTypeSelectorView.Type.VOICE_BROADCAST, - vectorFeatures.isVoiceBroadcastEnabled(), // TODO check user permission + vectorPreferences.isVoiceBroadcastEnabled(), // TODO check user permission ) } attachmentTypeSelector.show(composer.attachmentButton) diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt index 89fcda142a..2dc8b12160 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt @@ -74,6 +74,7 @@ class VectorPreferences @Inject constructor( const val SETTINGS_LABS_RICH_TEXT_EDITOR_KEY = "SETTINGS_LABS_RICH_TEXT_EDITOR_KEY" const val SETTINGS_LABS_NEW_SESSION_MANAGER_KEY = "SETTINGS_LABS_NEW_SESSION_MANAGER_KEY" const val SETTINGS_LABS_CLIENT_INFO_RECORDING_KEY = "SETTINGS_LABS_CLIENT_INFO_RECORDING_KEY" + const val SETTINGS_LABS_VOICE_BROADCAST_KEY = "SETTINGS_LABS_VOICE_BROADCAST_KEY" const val SETTINGS_CRYPTOGRAPHY_PREFERENCE_KEY = "SETTINGS_CRYPTOGRAPHY_PREFERENCE_KEY" const val SETTINGS_CRYPTOGRAPHY_DIVIDER_PREFERENCE_KEY = "SETTINGS_CRYPTOGRAPHY_DIVIDER_PREFERENCE_KEY" const val SETTINGS_CRYPTOGRAPHY_MANAGE_PREFERENCE_KEY = "SETTINGS_CRYPTOGRAPHY_MANAGE_PREFERENCE_KEY" @@ -1203,4 +1204,9 @@ class VectorPreferences @Inject constructor( fun isRichTextEditorEnabled(): Boolean { return defaultPrefs.getBoolean(SETTINGS_LABS_RICH_TEXT_EDITOR_KEY, getDefault(R.bool.settings_labs_rich_text_editor_default)) } + + fun isVoiceBroadcastEnabled(): Boolean { + return vectorFeatures.isVoiceBroadcastEnabled() && + defaultPrefs.getBoolean(SETTINGS_LABS_VOICE_BROADCAST_KEY, getDefault(R.bool.settings_labs_enable_voice_broadcast_default)) + } } diff --git a/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt index 6c31e32567..c10411301f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt @@ -16,6 +16,7 @@ package im.vector.app.features.settings.labs +import android.os.Build import android.os.Bundle import android.text.method.LinkMovementMethod import android.widget.TextView @@ -90,6 +91,11 @@ class VectorSettingsLabsFragment : } } + findPreference(VectorPreferences.SETTINGS_LABS_VOICE_BROADCAST_KEY)?.let { pref -> + // Voice Broadcast recording is not available on Android < 10 + pref.isVisible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && vectorFeatures.isVoiceBroadcastEnabled() + } + configureUnreadNotificationsAsTabPreference() configureEnableClientInfoRecordingPreference() } diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt index 3a9aac12d5..d445dfdfbe 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt @@ -22,5 +22,5 @@ object VoiceBroadcastConstants { const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info" /** Default voice broadcast chunk duration, in seconds. */ - const val DEFAULT_CHUNK_LENGTH_IN_SECONDS = 30 + const val DEFAULT_CHUNK_LENGTH_IN_SECONDS = 120 } diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt index 2668501a8d..37ff920c57 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt @@ -16,6 +16,7 @@ package im.vector.app.features.voicebroadcast +import androidx.annotation.IntRange import im.vector.app.features.voice.VoiceRecorder import java.io.File @@ -26,6 +27,6 @@ interface VoiceBroadcastRecorder : VoiceRecorder { fun startRecord(roomId: String, chunkLength: Int) fun interface Listener { - fun onVoiceMessageCreated(file: File) + fun onVoiceMessageCreated(file: File, @IntRange(from = 1) sequence: Int) } } diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt index 620db721c9..404b112574 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt @@ -29,6 +29,7 @@ class VoiceBroadcastRecorderQ( ) : AbstractVoiceRecorderQ(context), VoiceBroadcastRecorder { private var maxFileSize = 0L // zero or negative for no limit + private var currentSequence = 0 override var listener: VoiceBroadcastRecorder.Listener? = null @@ -51,6 +52,7 @@ class VoiceBroadcastRecorderQ( override fun startRecord(roomId: String, chunkLength: Int) { maxFileSize = (chunkLength * audioEncodingBitRate / 8).toLong() + currentSequence = 1 startRecord(roomId) } @@ -58,6 +60,7 @@ class VoiceBroadcastRecorderQ( super.stopRecord() notifyOutputFileCreated() listener = null + currentSequence = 0 } override fun release() { @@ -71,11 +74,12 @@ class VoiceBroadcastRecorderQ( private fun onNextOutputFileStarted() { notifyOutputFileCreated() + currentSequence++ } private fun notifyOutputFileCreated() { outputFile?.let { - listener?.onVoiceMessageCreated(it) + listener?.onVoiceMessageCreated(it, currentSequence) outputFile = nextOutputFile nextOutputFile = null } diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt index 2a306bcd28..780150d5e7 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt @@ -79,20 +79,21 @@ class StartVoiceBroadcastUseCase @Inject constructor( } private fun startRecording(room: Room, eventId: String, chunkLength: Int) { - voiceBroadcastRecorder?.listener = VoiceBroadcastRecorder.Listener { file -> - sendVoiceFile(room, file, eventId) + voiceBroadcastRecorder?.listener = VoiceBroadcastRecorder.Listener { file, sequence -> + sendVoiceFile(room, file, eventId, sequence) } voiceBroadcastRecorder?.startRecord(room.roomId, chunkLength) } - private fun sendVoiceFile(room: Room, voiceMessageFile: File, referenceEventId: String) { + private fun sendVoiceFile(room: Room, voiceMessageFile: File, referenceEventId: String, sequence: Int) { val outputFileUri = FileProvider.getUriForFile( context, buildMeta.applicationId + ".fileProvider", voiceMessageFile, - "Voice message.${voiceMessageFile.extension}" + "Voice Broadcast Part ($sequence).${voiceMessageFile.extension}" ) val audioType = outputFileUri.toMultiPickerAudioType(context) ?: return + // TODO put sequence in event content room.sendService().sendMedia( attachment = audioType.toContentAttachmentData(isVoiceMessage = true), compressBeforeSending = false, diff --git a/vector/src/main/res/xml/vector_settings_labs.xml b/vector/src/main/res/xml/vector_settings_labs.xml index 5b519bdd91..15a255753a 100644 --- a/vector/src/main/res/xml/vector_settings_labs.xml +++ b/vector/src/main/res/xml/vector_settings_labs.xml @@ -117,4 +117,11 @@ android:title="@string/labs_enable_client_info_recording_title" app:isPreferenceVisible="@bool/settings_labs_client_info_recording_visible" /> + +