Added export feature for call recordings

This commit is contained in:
Sylvain Berfini 2022-06-23 14:32:26 +02:00
parent d630f0ceec
commit 61454f5923
8 changed files with 65 additions and 13 deletions

View file

@ -19,6 +19,7 @@ Group changes to describe their impact on the project, as follows:
- Image & Video in-app viewers allow for full-screen display
- Display name can be set during assistant when creating / logging in a sip.linphone.org account
- Android 13 support, using new post notifications & media permissions
- Call recordings can be exported
### Changed
- In-call views have been re-designed

View file

@ -163,6 +163,10 @@ class RecordingData(val path: String, private val recordingListener: RecordingLi
}
}
fun export() {
recordingListener.onExportClicked(path)
}
private fun initPlayer() {
// In case no headphones/headset is connected, use speaker sound card to play recordings, otherwise use earpiece
// If none are available, default one will be used
@ -218,5 +222,6 @@ class RecordingData(val path: String, private val recordingListener: RecordingLi
interface RecordingListener {
fun onPlayingStarted(videoAvailable: Boolean)
fun onPlayingEnded()
fun onExportClicked(path: String)
}
}

View file

@ -19,6 +19,8 @@
*/
package org.linphone.activities.main.recordings.fragments
import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Bundle
import android.view.MotionEvent
import android.view.View
@ -32,6 +34,7 @@ import org.linphone.activities.main.recordings.viewmodels.RecordingsViewModel
import org.linphone.core.tools.Log
import org.linphone.databinding.RecordingsFragmentBinding
import org.linphone.utils.AppUtils
import org.linphone.utils.FileUtils
import org.linphone.utils.RecyclerViewHeaderDecoration
class RecordingsFragment : MasterFragment<RecordingsFragmentBinding, RecordingsListAdapter>() {
@ -75,6 +78,25 @@ class RecordingsFragment : MasterFragment<RecordingsFragmentBinding, RecordingsL
adapter.submitList(recordings)
}
viewModel.exportRecordingEvent.observe(
viewLifecycleOwner
) {
it.consume { path ->
val publicFilePath = FileUtils.getPublicFilePath(requireContext(), "file://$path")
Log.i("[Recordings] Exporting file [$path] with public URI [$publicFilePath]")
val intent = Intent(Intent.ACTION_SEND)
intent.type = " video/x-matroska"
intent.putExtra(Intent.EXTRA_STREAM, publicFilePath)
intent.putExtra(Intent.EXTRA_TEXT, getString(R.string.recordings_export))
try {
requireActivity().startActivity(Intent.createChooser(intent, getString(R.string.recordings_export)))
} catch (anfe: ActivityNotFoundException) {
Log.e(anfe)
}
}
}
binding.setBackClickListener { goBack() }
binding.setEditClickListener { listSelectionViewModel.isEditionEnabled.value = true }

View file

@ -27,6 +27,7 @@ import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.activities.main.recordings.data.RecordingData
import org.linphone.core.tools.Log
import org.linphone.utils.AppUtils
import org.linphone.utils.Event
import org.linphone.utils.FileUtils
class RecordingsViewModel : ViewModel() {
@ -34,6 +35,10 @@ class RecordingsViewModel : ViewModel() {
val isVideoVisible = MutableLiveData<Boolean>()
val exportRecordingEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
private var recordingPlayingAudioFocusRequest: AudioFocusRequestCompat? = null
private val recordingListener = object : RecordingData.RecordingListener {
@ -56,6 +61,10 @@ class RecordingsViewModel : ViewModel() {
isVideoVisible.value = false
}
override fun onExportClicked(path: String) {
exportRecordingEvent.value = Event(path)
}
}
init {
@ -93,7 +102,7 @@ class RecordingsViewModel : ViewModel() {
val list = arrayListOf<RecordingData>()
for (f in FileUtils.getFileStorageDir().listFiles().orEmpty()) {
Log.i("[Recordings] Found file ${f.path}")
Log.d("[Recordings] Found file ${f.path}")
if (RecordingData.RECORD_PATTERN.matcher(f.path).matches()) {
list.add(
RecordingData(

View file

@ -58,7 +58,7 @@
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_toLeftOf="@id/delete"
android:layout_toLeftOf="@id/export"
android:layout_toRightOf="@id/record_play_pause">
<LinearLayout
@ -70,9 +70,9 @@
android:layout_alignParentTop="true">
<TextView
android:text="@{data.name}"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
android:text="@{data.name}" />
<TextView
android:layout_width="wrap_content"
@ -80,9 +80,9 @@
android:text=" - " />
<TextView
android:text="@{data.formattedDate}"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
android:text="@{data.formattedDate}" />
</LinearLayout>
@ -96,10 +96,10 @@
android:orientation="horizontal">
<TextView
android:text="@{data.formattedPosition}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
android:layout_gravity="center"
android:text="@{data.formattedPosition}" />
<TextView
android:layout_width="wrap_content"
@ -107,27 +107,37 @@
android:text="/" />
<TextView
android:text="@{data.formattedDuration}"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
android:text="@{data.formattedDuration}" />
</LinearLayout>
<SeekBar
android:id="@+id/record_progression_bar"
onProgressChanged="@{(progress) -> data.onProgressChanged(progress)}"
android:max="@{data.duration}"
android:progress="@{data.position}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_marginRight="5dp"
android:layout_alignParentEnd="true"
android:max="@{data.duration}"
android:progress="@{data.position}"
android:progressTint="?attr/accentColor"
android:thumbTint="?attr/accentColor" />
</RelativeLayout>
<ImageView
android:id="@+id/export"
android:layout_width="30dp"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:onClick="@{() -> data.export()}"
android:src="@drawable/icon_share" />
</RelativeLayout>
</layout>

View file

@ -731,4 +731,6 @@
<string name="chat_message_context_menu_turn_on_notifications">Activer les notifications</string>
<string name="content_description_muted_chat_room">Les notifications sont désactivées pour cette conversation</string>
<string name="chat_room_context_menu_go_to_contact">Voir le contact</string>
<string name="recordings_export">Exporter l\'enregistrement avec…</string>
<string name="content_description_recording_export">Exporter l\'enregistrement</string>
</resources>

View file

@ -234,6 +234,7 @@
<!-- Recordings -->
<string name="recordings_empty_list">No recordings</string>
<string name="recordings_export">Export recording using…</string>
<!-- Conferencing -->
<string name="conference">Meeting</string>
@ -861,4 +862,5 @@
<string name="content_description_participant_is_paused">Participant has momentarily left the group call</string>
<string name="content_description_toggle_conference_info_details">Toggle meeting information details visibility</string>
<string name="content_description_conference_participants">Group call participants</string>
<string name="content_description_recording_export">Export recording</string>
</resources>

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path name="internal_files" path="." />
<files-path name="internal_recordings" path="Download/" />
<external-files-path name="pictures" path="Pictures/" />
<external-files-path name="downloads" path="Download/" />
<external-files-path name="files" path="." />