Work on call recording

This commit is contained in:
Juha Heinanen
2023-03-10 06:26:16 +02:00
parent 85d9babee3
commit 9ab8c8f33e
8 changed files with 67 additions and 63 deletions
@@ -59,7 +59,7 @@ class AccountListAdapter(private val cxt: Context, private val rows: ArrayList<A
viewHolder.aorView.text)) viewHolder.aorView.text))
setPositiveButton(cxt.getText(R.string.delete)) { dialog, _ -> setPositiveButton(cxt.getText(R.string.delete)) { dialog, _ ->
Api.ua_destroy(ua.uap) Api.ua_destroy(ua.uap)
NewCallHistory.clear(ua.account.aor) CallHistory.clear(ua.account.aor)
Message.clear(ua.account.aor) Message.clear(ua.account.aor)
ua.remove() ua.remove()
AccountsActivity.generateAccounts() AccountsActivity.generateAccounts()
@@ -43,7 +43,6 @@ import androidx.media.AudioManagerCompat
import java.io.File import java.io.File
import java.net.InetAddress import java.net.InetAddress
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.text.SimpleDateFormat
import java.util.* import java.util.*
import kotlin.concurrent.schedule import kotlin.concurrent.schedule
import kotlin.math.roundToInt import kotlin.math.roundToInt
@@ -411,18 +410,17 @@ class BaresipService: Service() {
} }
Contact.contactsUpdate() Contact.contactsUpdate()
val history = CallHistory.get() val history = NewCallHistory.get()
if (history.isEmpty()) { if (history.isEmpty()) {
NewCallHistory.restore() CallHistory.restore()
} else { } else {
for (old in history) { for (old in history) {
val new = NewCallHistory(old.aor, old.peerUri, old.direction) val new = CallHistory(old.aor, old.peerUri, old.direction)
new.stopTime = old.time new.stopTime = old.stopTime
if (old.connected) new.startTime = old.startTime
new.startTime = GregorianCalendar(0, 0, 0)
callHistory.add(new) callHistory.add(new)
} }
NewCallHistory.save() CallHistory.save()
} }
Message.restore() Message.restore()
@@ -593,6 +591,8 @@ class BaresipService: Service() {
if (ev[0] == "sndfile dump") { if (ev[0] == "sndfile dump") {
Log.d(TAG, "Got sndfile dump ${ev[1]}") Log.d(TAG, "Got sndfile dump ${ev[1]}")
if (Call.calls().size > 0)
Call.calls()[0].dumpfile = ev[1]
return return
} }
@@ -679,8 +679,8 @@ class BaresipService: Service() {
if (!Utils.checkPermissions(this, arrayOf(Manifest.permission.RECORD_AUDIO))) { if (!Utils.checkPermissions(this, arrayOf(Manifest.permission.RECORD_AUDIO))) {
toast(getString(R.string.no_calls)) toast(getString(R.string.no_calls))
if (ua.account.callHistory) { if (ua.account.callHistory) {
NewCallHistory.add(NewCallHistory(aor, peerUri, "in")) CallHistory.add(CallHistory(aor, peerUri, "in"))
NewCallHistory.save() CallHistory.save()
ua.account.missedCalls = true ua.account.missedCalls = true
} }
if (!isMainVisible) if (!isMainVisible)
@@ -761,8 +761,8 @@ class BaresipService: Service() {
"call rejected" -> { "call rejected" -> {
playUnInterrupted(R.raw.callwaiting, 1) playUnInterrupted(R.raw.callwaiting, 1)
if (ua.account.callHistory) { if (ua.account.callHistory) {
NewCallHistory.add(NewCallHistory(aor, ev[1], "in")) CallHistory.add(CallHistory(aor, ev[1], "in"))
NewCallHistory.save() CallHistory.save()
ua.account.missedCalls = true ua.account.missedCalls = true
if (!isMainVisible) if (!isMainVisible)
return return
@@ -894,29 +894,17 @@ class BaresipService: Service() {
} }
val missed = call.startTime == null && call.dir == "in" && !call.rejected val missed = call.startTime == null && call.dir == "in" && !call.rejected
if (ua.account.callHistory) { if (ua.account.callHistory) {
val history = NewCallHistory(aor, call.peerUri, call.dir) val history = CallHistory(aor, call.peerUri, call.dir)
history.startTime = call.startTime history.startTime = call.startTime
history.stopTime = GregorianCalendar() history.stopTime = GregorianCalendar()
NewCallHistory.add(history) if (call.startTime != null && call.dumpfile != "") {
NewCallHistory.save() val recording = filesDir.absolutePath + "/recordings/" +
call.dumpfile.substringBeforeLast("-")
history.recording = recording
}
CallHistory.add(history)
CallHistory.save()
ua.account.missedCalls = ua.account.missedCalls || missed ua.account.missedCalls = ua.account.missedCalls || missed
if (isRecOn && call.startTime != null) {
val format = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss")
val startTime = call.startTime!!
var time = format.format(startTime.time).toString()
var recording = filesDir.absolutePath + "/recordings/" +
"dump-$aor=>${call.peerUri}-$time"
if (File("$recording-dec.wav").exists()) {
recordings[time] = recording
} else {
startTime.add(Calendar.SECOND, 1)
time = format.format(startTime.time).toString()
recording = filesDir.absolutePath + "/recordings/" +
"dump-$aor=>${call.peerUri}-$time"
recordings[time] = recording
}
}
} }
if (!Utils.isVisible()) { if (!Utils.isVisible()) {
if (missed) { if (missed) {
@@ -1540,7 +1528,7 @@ class BaresipService: Service() {
val uas = ArrayList<UserAgent>() val uas = ArrayList<UserAgent>()
val calls = ArrayList<Call>() val calls = ArrayList<Call>()
var callHistory = ArrayList<NewCallHistory>() var callHistory = ArrayList<CallHistory>()
var messages = ArrayList<Message>() var messages = ArrayList<Message>()
val messageUpdate = MutableLiveData<Long>() val messageUpdate = MutableLiveData<Long>()
val contactUpdate = MutableLiveData<Long>() val contactUpdate = MutableLiveData<Long>()
@@ -15,6 +15,7 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str
var zid = "" var zid = ""
var startTime: GregorianCalendar? = null // Set when call is established var startTime: GregorianCalendar? = null // Set when call is established
var referTo = "" var referTo = ""
var dumpfile = ""
fun add() { fun add() {
BaresipService.calls.add(0, this) BaresipService.calls.add(0, this)
@@ -18,7 +18,8 @@ import java.util.*
class CallDetailsAdapter(private val ctx: Context, private val rows: ArrayList<CallRow.Details>) : class CallDetailsAdapter(private val ctx: Context, private val rows: ArrayList<CallRow.Details>) :
ArrayAdapter<CallRow.Details>(ctx, R.layout.call_detail_row, rows) { ArrayAdapter<CallRow.Details>(ctx, R.layout.call_detail_row, rows) {
private val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater private val layoutInflater =
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
private class ViewHolder(view: View?) { private class ViewHolder(view: View?) {
val directionView = view?.findViewById(R.id.direction) as ImageView val directionView = view?.findViewById(R.id.direction) as ImageView
@@ -72,12 +73,11 @@ class CallDetailsAdapter(private val ctx: Context, private val rows: ArrayList<C
viewHolder.timeView.text = startText viewHolder.timeView.text = startText
val duration = (stopTime.time.time - startTime.time.time) / 1000 val duration = (stopTime.time.time - startTime.time.time) / 1000
viewHolder.durationView.text = DateUtils.formatElapsedTime(duration) viewHolder.durationView.text = DateUtils.formatElapsedTime(duration)
val format = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss") val recording = rows[position].recording
val time = format.format(startTime.time).toString() if (recording != "") {
if (BaresipService.recordings.containsKey(time)) {
viewHolder.durationView.typeface = Typeface.DEFAULT_BOLD viewHolder.durationView.typeface = Typeface.DEFAULT_BOLD
viewHolder.durationView.setOnClickListener { viewHolder.durationView.setOnClickListener {
Utils.playRecording(ctx, BaresipService.recordings[time]!!) Utils.playRecording(ctx, recording)
} }
} }
} }
@@ -85,5 +85,4 @@ class CallDetailsAdapter(private val ctx: Context, private val rows: ArrayList<C
return rowView return rowView
} }
} }
@@ -4,17 +4,18 @@ import java.io.*
import java.util.ArrayList import java.util.ArrayList
import java.util.GregorianCalendar import java.util.GregorianCalendar
class NewCallHistory(val aor: String, val peerUri: String, val direction: String) : Serializable { class CallHistory(val aor: String, val peerUri: String, val direction: String) : Serializable {
var startTime: GregorianCalendar? = null // Set to time when call is established (if ever) var startTime: GregorianCalendar? = null // Set to time when call is established (if ever)
var stopTime = GregorianCalendar() // Set to time when call is closed var stopTime = GregorianCalendar() // Set to time when call is closed
var recording = ""
companion object { companion object {
private const val serialVersionUID: Long = 1 private const val serialVersionUID: Long = 2
private const val CALL_HISTORY_SIZE = 128 private const val CALL_HISTORY_SIZE = 128
fun add(history: NewCallHistory) { fun add(history: CallHistory) {
BaresipService.callHistory.add(history) BaresipService.callHistory.add(history)
if (aorHistorySize(history.aor) > CALL_HISTORY_SIZE) { if (aorHistorySize(history.aor) > CALL_HISTORY_SIZE) {
var i = 0 var i = 0
@@ -44,7 +45,7 @@ class NewCallHistory(val aor: String, val peerUri: String, val direction: String
fun save() { fun save() {
Log.d(TAG, "Saving history of ${BaresipService.callHistory.size} calls") Log.d(TAG, "Saving history of ${BaresipService.callHistory.size} calls")
val file = File(BaresipService.filesPath, "call_history") val file = File(BaresipService.filesPath, "history")
try { try {
val fos = FileOutputStream(file) val fos = FileOutputStream(file)
val oos = ObjectOutputStream(fos) val oos = ObjectOutputStream(fos)
@@ -58,13 +59,13 @@ class NewCallHistory(val aor: String, val peerUri: String, val direction: String
} }
fun restore() { fun restore() {
val file = File(BaresipService.filesPath, "call_history") val file = File(BaresipService.filesPath, "history")
if (file.exists()) { if (file.exists()) {
try { try {
val fis = FileInputStream(file) val fis = FileInputStream(file)
val ois = ObjectInputStream(fis) val ois = ObjectInputStream(fis)
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
BaresipService.callHistory = ois.readObject() as ArrayList<NewCallHistory> BaresipService.callHistory = ois.readObject() as ArrayList<CallHistory>
ois.close() ois.close()
fis.close() fis.close()
Log.d(TAG, "Restored history of ${BaresipService.callHistory.size} calls") Log.d(TAG, "Restored history of ${BaresipService.callHistory.size} calls")
@@ -77,28 +78,31 @@ class NewCallHistory(val aor: String, val peerUri: String, val direction: String
@Suppress("UNUSED") @Suppress("UNUSED")
fun print() { fun print() {
for (h in BaresipService.callHistory) for (h in BaresipService.callHistory)
Log.d(TAG, "[${h.aor}, ${h.peerUri}, ${h.direction}, ${h.startTime}, ${h.stopTime}]") Log.d(TAG, "[${h.aor}, ${h.peerUri}, ${h.direction}, ${h.startTime}," +
"${h.stopTime}, ${h.recording}]")
} }
} }
} }
class CallHistory(val aor: String, val peerUri: String, val direction: String, class NewCallHistory(val aor: String, val peerUri: String, val direction: String) : Serializable {
val connected: Boolean) : Serializable {
val time: GregorianCalendar = GregorianCalendar() var startTime: GregorianCalendar? = null
var stopTime = GregorianCalendar()
companion object { companion object {
fun get(): ArrayList<CallHistory> { private const val serialVersionUID: Long = 1
val file = File(BaresipService.filesPath, "calls")
var result = ArrayList<CallHistory>() fun get(): ArrayList<NewCallHistory> {
val file = File(BaresipService.filesPath, "call_history")
var result = ArrayList<NewCallHistory>()
if (file.exists()) { if (file.exists()) {
try { try {
val fis = FileInputStream(file) val fis = FileInputStream(file)
val ois = ObjectInputStream(fis) val ois = ObjectInputStream(fis)
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
result = ois.readObject() as ArrayList<CallHistory> result = ois.readObject() as ArrayList<NewCallHistory>
ois.close() ois.close()
fis.close() fis.close()
Log.d(TAG, "Got history of ${result.size} calls") Log.d(TAG, "Got history of ${result.size} calls")
@@ -4,14 +4,19 @@ import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
class CallRow(val aor: String, val peerUri: String, val direction: Int, class CallRow(val aor: String, val peerUri: String, val direction: Int,
startTime: GregorianCalendar?, val stopTime: GregorianCalendar) { startTime: GregorianCalendar?,
val stopTime: GregorianCalendar,
private val recording: String)
class Details(val direction: Int, val startTime: GregorianCalendar?, val stopTime: GregorianCalendar) {
class Details(val direction: Int, val startTime: GregorianCalendar?,
val stopTime: GregorianCalendar, val recording: String)
val details = ArrayList<Details>() val details = ArrayList<Details>()
init { init {
details.add(Details(direction, startTime, stopTime)) details.add(Details(direction, startTime, stopTime, recording))
} }
} }
@@ -114,7 +114,7 @@ class CallsActivity : AppCompatActivity() {
} }
DialogInterface.BUTTON_POSITIVE -> { DialogInterface.BUTTON_POSITIVE -> {
removeUaHistoryAt(pos) removeUaHistoryAt(pos)
NewCallHistory.save() CallHistory.save()
clAdapter.notifyDataSetChanged() clAdapter.notifyDataSetChanged()
} }
DialogInterface.BUTTON_NEUTRAL -> { DialogInterface.BUTTON_NEUTRAL -> {
@@ -168,8 +168,8 @@ class CallsActivity : AppCompatActivity() {
setMessage(String.format(getString(R.string.delete_history_alert), setMessage(String.format(getString(R.string.delete_history_alert),
aor.substringAfter(":"))) aor.substringAfter(":")))
setPositiveButton(getText(R.string.delete)) { dialog, _ -> setPositiveButton(getText(R.string.delete)) { dialog, _ ->
NewCallHistory.clear(aor) CallHistory.clear(aor)
NewCallHistory.save() CallHistory.save()
aorGenerateHistory(aor) aorGenerateHistory(aor)
clAdapter.notifyDataSetChanged() clAdapter.notifyDataSetChanged()
dialog.dismiss() dialog.dismiss()
@@ -244,9 +244,11 @@ class CallsActivity : AppCompatActivity() {
else else
R.drawable.arrow_up_red R.drawable.arrow_up_red
if (uaHistory.isNotEmpty() && (uaHistory.last().peerUri == h.peerUri)) if (uaHistory.isNotEmpty() && (uaHistory.last().peerUri == h.peerUri))
uaHistory.last().details.add(CallRow.Details(direction, h.startTime, h.stopTime)) uaHistory.last().details.add(CallRow.Details(direction, h.startTime,
h.stopTime, h.recording))
else else
uaHistory.add(CallRow(h.aor, h.peerUri, direction, h.startTime, h.stopTime)) uaHistory.add(CallRow(h.aor, h.peerUri, direction, h.startTime,
h.stopTime, h.recording))
} }
} }
} }
@@ -1200,6 +1200,11 @@ class MainActivity : AppCompatActivity() {
} else { } else {
callTimer.stop() callTimer.stop()
} }
if (BaresipService.isRecOn) {
Api.module_unload("sndfile")
BaresipService.isRecOn = false
recIcon!!.setIcon(R.drawable.rec_off)
}
if (aor == aorSpinner.tag) { if (aor == aorSpinner.tag) {
callUri.inputType = InputType.TYPE_CLASS_TEXT + callUri.inputType = InputType.TYPE_CLASS_TEXT +
InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
@@ -1892,7 +1897,7 @@ class MainActivity : AppCompatActivity() {
} }
} }
} else { } else {
val latestPeerUri = NewCallHistory.aorLatestPeerUri(aor) val latestPeerUri = CallHistory.aorLatestPeerUri(aor)
if (latestPeerUri != null) if (latestPeerUri != null)
callUri.setText(Utils.friendlyUri(this, latestPeerUri, ua.account)) callUri.setText(Utils.friendlyUri(this, latestPeerUri, ua.account))
} }