Added call recording

This commit is contained in:
Sylvain Berfini 2023-09-29 11:18:08 +02:00
parent 95e8ef9fc4
commit bdca32be49
10 changed files with 71 additions and 8 deletions

View file

@ -42,6 +42,7 @@ import org.linphone.ui.call.CallActivity
import org.linphone.utils.ActivityMonitor
import org.linphone.utils.AppUtils
import org.linphone.utils.Event
import org.linphone.utils.LinphoneUtils
class CoreContext @UiThread constructor(val context: Context) : HandlerThread("Core Thread") {
companion object {
@ -280,8 +281,9 @@ class CoreContext @UiThread constructor(val context: Context) : HandlerThread("C
/*if (LinphoneUtils.checkIfNetworkHasLowBandwidth(context)) {
Log.w("[Context] Enabling low bandwidth mode!")
params.isLowBandwidthEnabled = true
}
params.recordFile = LinphoneUtils.getRecordingFilePathForAddress(address)*/
}*/
params.recordFile = LinphoneUtils.getRecordingFilePathForAddress(address)
if (localAddress != null) {
val account = core.accountList.find { account ->
@ -299,10 +301,6 @@ class CoreContext @UiThread constructor(val context: Context) : HandlerThread("C
}
}
/*if (corePreferences.sendEarlyMedia) {
params.isEarlyMediaSendingEnabled = true
}*/
val call = core.inviteAddressWithParams(address, params)
Log.i("$TAG Starting call $call")
}
@ -343,7 +341,7 @@ class CoreContext @UiThread constructor(val context: Context) : HandlerThread("C
return
}
// params.recordFile = LinphoneUtils.getRecordingFilePathForAddress(call.remoteAddress)
params.recordFile = LinphoneUtils.getRecordingFilePathForAddress(call.remoteAddress)
/*if (LinphoneUtils.checkIfNetworkHasLowBandwidth(context)) {
Log.w("$TAG Enabling low bandwidth mode!")

View file

@ -298,7 +298,7 @@ class CallActivity : AppCompatActivity() {
}
}
private fun showGreenToast(message: String, @DrawableRes icon: Int, duration: Long = 4000) {
fun showGreenToast(message: String, @DrawableRes icon: Int, duration: Long = 4000) {
val greenToast = AppUtils.getGreenToast(this, binding.toastsArea, message, icon)
binding.toastsArea.addView(greenToast.root)

View file

@ -228,6 +228,15 @@ class ActiveCallFragment : GenericCallFragment() {
}
}
callViewModel.isRecording.observe(viewLifecycleOwner) { recording ->
val text = if (recording) {
getString(R.string.toast_call_recording_started)
} else {
getString(R.string.toast_call_recording_stopped)
}
(requireActivity() as CallActivity).showGreenToast(text, R.drawable.record)
}
actionsBottomSheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
when (newState) {

View file

@ -72,6 +72,8 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() {
val isOutgoing = MutableLiveData<Boolean>()
val isRecording = MutableLiveData<Boolean>()
val isMicrophoneMuted = MutableLiveData<Boolean>()
val isSpeakerEnabled = MutableLiveData<Boolean>()
@ -503,6 +505,23 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() {
}
}
@UiThread
fun toggleRecording() {
coreContext.postOnCoreThread {
if (::currentCall.isInitialized) {
if (currentCall.params.isRecording) {
Log.i("$TAG Stopping call recording")
currentCall.stopRecording()
} else {
Log.i("$TAG Starting call recording")
currentCall.startRecording()
}
val recording = currentCall.params.isRecording
isRecording.postValue(recording)
}
}
}
@UiThread
fun toggleFullScreen() {
if (fullScreenMode.value == false && isVideoEnabled.value == false) return
@ -611,6 +630,11 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() {
isOutgoing.postValue(call.dir == Call.Dir.Outgoing)
if (call.params.isRecording) {
// Do not set it to false to prevent the "no longer recording" toast to be displayed
isRecording.postValue(true)
}
val address = call.remoteAddress.clone()
address.clean()
displayedAddress.postValue(address.asStringUriOnly())

View file

@ -22,6 +22,10 @@ package org.linphone.utils
import androidx.annotation.AnyThread
import androidx.annotation.IntegerRes
import androidx.annotation.WorkerThread
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.core.Account
@ -37,6 +41,8 @@ class LinphoneUtils {
companion object {
private const val TAG = "[Linphone Utils]"
private const val RECORDING_DATE_PATTERN = "dd-MM-yyyy-HH-mm-ss"
@WorkerThread
fun getDefaultAccount(): Account? {
return coreContext.core.defaultAccount ?: coreContext.core.accountList.firstOrNull()
@ -192,6 +198,17 @@ class LinphoneUtils {
return "${localSipUri.asStringUriOnly()}~${remoteSipUri.asStringUriOnly()}"
}
@WorkerThread
fun getRecordingFilePathForAddress(address: Address): String {
val displayName = getDisplayName(address)
val dateFormat: DateFormat = SimpleDateFormat(
RECORDING_DATE_PATTERN,
Locale.getDefault()
)
val fileName = "${displayName}_${dateFormat.format(Date())}.mkv"
return FileUtils.getFileStoragePath(fileName).absolutePath
}
@WorkerThread
fun callStateToString(state: Call.State): String {
return when (state) {

View file

@ -2,6 +2,8 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:drawable="@drawable/shape_round_in_call_disabled_button_background" />
<item android:state_selected="true"
android:drawable="@drawable/shape_round_in_call_selected_button_background" />
<item android:state_pressed="true"
android:drawable="@drawable/shape_round_in_call_pressed_button_background" />
<item

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<size android:width="55dp" android:height="55dp" />
<solid android:color="@color/red_danger_500"/>
</shape>

View file

@ -147,12 +147,14 @@
<ImageView
android:id="@+id/record_call"
android:onClick="@{() -> viewModel.toggleRecording()}"
android:layout_width="0dp"
android:layout_height="@dimen/call_button_size"
android:layout_marginTop="@dimen/call_extra_button_top_margin"
android:background="@drawable/in_call_button_background"
android:padding="@dimen/call_button_icon_padding"
android:src="@drawable/record"
android:selected="@{viewModel.isRecording()}"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="@id/record_call_label"
app:layout_constraintStart_toStartOf="@id/record_call_label"
@ -232,6 +234,7 @@
<androidx.appcompat.widget.AppCompatTextView
style="@style/in_call_extra_action_label_style"
android:id="@+id/record_call_label"
android:onClick="@{() -> viewModel.toggleRecording()}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingBottom="15dp"

View file

@ -147,12 +147,14 @@
<ImageView
android:id="@+id/record_call"
android:onClick="@{() -> viewModel.toggleRecording()}"
android:layout_width="0dp"
android:layout_height="@dimen/call_button_size"
android:layout_marginTop="@dimen/call_extra_button_top_margin"
android:padding="@dimen/call_button_icon_padding"
android:background="@drawable/in_call_button_background"
android:src="@drawable/record"
android:selected="@{viewModel.isRecording()}"
app:tint="@color/white"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toBottomOf="@id/calls_list_label"
@ -228,6 +230,7 @@
<androidx.appcompat.widget.AppCompatTextView
style="@style/in_call_extra_action_label_style"
android:id="@+id/record_call_label"
android:onClick="@{() -> viewModel.toggleRecording()}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingBottom="15dp"

View file

@ -301,6 +301,8 @@
<string name="toast_call_transfer_in_progress">Call is being transferred to %s</string>
<string name="toast_call_transfer_successful">Call has been transferred to %s</string>
<string name="toast_call_transfer_failed">Call transfer to %s failed!</string>
<string name="toast_call_recording_started">Call is being recorded</string>
<string name="toast_call_recording_stopped">Call is no longer being recorded</string>
<!-- Keep <u></u> in following strings translations! -->
<string name="welcome_carousel_skip"><u>Skip</u></string>