diff --git a/app/src/main/kotlin/com/tutpro/baresip/AccountListAdapter.kt b/app/src/main/kotlin/com/tutpro/baresip/AccountListAdapter.kt index 084d000d..fa66729e 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/AccountListAdapter.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/AccountListAdapter.kt @@ -61,7 +61,7 @@ class AccountListAdapter(private val cxt: Context, private val rows: ArrayList Api.ua_destroy(ua.uap) - CallHistory.clear(ua.account.aor) + NewCallHistory.clear(ua.account.aor) Message.clear(ua.account.aor) ua.remove() AccountsActivity.generateAccounts() diff --git a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt index eed0b898..a189326d 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt @@ -365,7 +365,20 @@ class BaresipService: Service() { File(filesDir, "history").renameTo(File(filesDir, "calls")) Contact.restore() - CallHistory.restore() + val history = CallHistory.get() + if (history.isEmpty()) { + NewCallHistory.restore() + } else { + for (old in history) { + val new = NewCallHistory(old.aor, old.peerUri, old.direction) + new.stopTime = old.time + if (old.connected) + new.startTime = GregorianCalendar(0, 0, 0) + callHistory.add(new) + } + NewCallHistory.save() + } + Message.restore() linkAddresses = linkAddresses() @@ -427,11 +440,8 @@ class BaresipService: Service() { val peerUri = call.peerUri val aor = call.ua.account.aor Log.d(TAG, "Aor $aor rejected incoming call $callp from $peerUri") + call.rejected = true Api.ua_hangup(call.ua.uap, callp, 486, "Rejected") - if (call.ua.account.callHistory) { - CallHistory.add(CallHistory(aor, peerUri, "in", false)) - CallHistory.save() - } } } @@ -603,11 +613,6 @@ class BaresipService: Service() { !Utils.checkPermissions(this, arrayOf(Manifest.permission.RECORD_AUDIO))) { Log.d(TAG, "Auto-rejecting incoming call $uap/$callp/$peerUri") Api.ua_hangup(uap, callp, 486, "Busy Here") - if (ua.account.callHistory) { - CallHistory.add(CallHistory(aor, peerUri, "in", false)) - CallHistory.save() - ua.account.missedCalls = true - } playUnInterrupted(R.raw.callwaiting, 1) if (!Utils.isVisible()) return @@ -707,11 +712,8 @@ class BaresipService: Service() { Log.d(TAG, "AoR $aor call $callp established") call.status = "connected" call.onhold = false - if (ua.account.callHistory) { - CallHistory.add(CallHistory(aor, call.peerUri, call.dir, true)) - CallHistory.save() - call.hasHistory = true - } + if (ua.account.callHistory) + call.startTime = GregorianCalendar() if (!Utils.isVisible()) return } @@ -832,12 +834,16 @@ class BaresipService: Service() { am.mode = AudioManager.MODE_NORMAL proximitySensing(false) } - if (ua.account.callHistory && !call.hasHistory) { - CallHistory.add(CallHistory(aor, call.peerUri, call.dir, false)) - CallHistory.save() - if (call.dir == "in") ua.account.missedCalls = true + val missed = call.startTime == null && call.dir == "in" && !call.rejected + if (ua.account.callHistory) { + val history = NewCallHistory(aor, call.peerUri, call.dir) + history.startTime = call.startTime + history.stopTime = GregorianCalendar() + NewCallHistory.add(history) + NewCallHistory.save() + ua.account.missedCalls = ua.account.missedCalls || missed } - if (!Utils.isVisible() && !call.hasHistory && call.dir == "in") { + if (!Utils.isVisible() && missed) { val caller = Utils.friendlyUri(ContactsActivity.contactName(call.peerUri), Utils.aorDomain(aor)) val intent = Intent(applicationContext, MainActivity::class.java) @@ -1446,7 +1452,7 @@ class BaresipService: Service() { val uas = ArrayList() val status = ArrayList() val calls = ArrayList() - var callHistory = ArrayList() + var callHistory = ArrayList() var messages = ArrayList() val contacts = ArrayList() val chatTexts: MutableMap = mutableMapOf() diff --git a/app/src/main/kotlin/com/tutpro/baresip/Call.kt b/app/src/main/kotlin/com/tutpro/baresip/Call.kt index 50ca42e2..af578a00 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Call.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Call.kt @@ -1,16 +1,17 @@ package com.tutpro.baresip import android.text.TextWatcher -import java.util.ArrayList +import java.util.* class Call(val callp: String, val ua: UserAgent, val peerUri: String, val dir: String, var status: String, val dtmfWatcher: TextWatcher?) { var onhold = false var held = false + var rejected = false var security = 0 var zid = "" - var hasHistory = false + var startTime: GregorianCalendar? = null // Set when call is established var referTo = "" fun add() { diff --git a/app/src/main/kotlin/com/tutpro/baresip/CallHistory.kt b/app/src/main/kotlin/com/tutpro/baresip/CallHistory.kt index 7ac3d352..5023b500 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/CallHistory.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/CallHistory.kt @@ -4,16 +4,17 @@ import java.io.* import java.util.ArrayList import java.util.GregorianCalendar -class CallHistory(val aor: String, val peerUri: String, val direction: String, - val connected: Boolean) : Serializable { +class NewCallHistory(val aor: String, val peerUri: String, val direction: String) : Serializable { - val time: GregorianCalendar = GregorianCalendar() + var startTime: GregorianCalendar? = null // Set to time when call is established (if ever) + var stopTime = GregorianCalendar() // Set to time when call is closed companion object { - private const val CALL_HISTORY_SIZE = 100 + private const val serialVersionUID: Long = 1 + private const val CALL_HISTORY_SIZE = 128 - fun add(history: CallHistory) { + fun add(history: NewCallHistory) { BaresipService.callHistory.add(history) if (aorHistorySize(history.aor) > CALL_HISTORY_SIZE) { var i = 0 @@ -35,7 +36,7 @@ class CallHistory(val aor: String, val peerUri: String, val direction: String, return BaresipService.callHistory.filter { it.aor == aor }.count() } - fun aorLatestHistory(aor: String): CallHistory? { + fun aorLatestHistory(aor: String): NewCallHistory? { for (h in BaresipService.callHistory.reversed()) if (h.aor == aor) return h return null @@ -43,7 +44,7 @@ class CallHistory(val aor: String, val peerUri: String, val direction: String, fun save() { Log.d(TAG, "Saving history of ${BaresipService.callHistory.size} calls") - val file = File(BaresipService.filesPath, "calls") + val file = File(BaresipService.filesPath, "call_history") try { val fos = FileOutputStream(file) val oos = ObjectOutputStream(fos) @@ -57,26 +58,57 @@ class CallHistory(val aor: String, val peerUri: String, val direction: String, } fun restore() { - val file = File(BaresipService.filesPath, "calls") - if (file.exists()) + val file = File(BaresipService.filesPath, "call_history") + if (file.exists()) { try { val fis = FileInputStream(file) val ois = ObjectInputStream(fis) @Suppress("UNCHECKED_CAST") - BaresipService.callHistory = ois.readObject() as ArrayList + BaresipService.callHistory = ois.readObject() as ArrayList ois.close() fis.close() Log.d(TAG, "Restored history of ${BaresipService.callHistory.size} calls") } catch (e: Exception) { Log.e(TAG, "InputStream exception: - $e") } + } } @Suppress("UNUSED") fun print() { for (h in BaresipService.callHistory) - Log.d(TAG, "[${h.aor}, ${h.peerUri}, ${h.direction}, ${h.connected}]") + Log.d(TAG, "[${h.aor}, ${h.peerUri}, ${h.direction}, ${h.startTime}, ${h.stopTime}]") } } } + +class CallHistory(val aor: String, val peerUri: String, val direction: String, + val connected: Boolean) : Serializable { + + val time: GregorianCalendar = GregorianCalendar() + + companion object { + + fun get(): ArrayList { + val file = File(BaresipService.filesPath, "calls") + var result = ArrayList() + if (file.exists()) { + try { + val fis = FileInputStream(file) + val ois = ObjectInputStream(fis) + @Suppress("UNCHECKED_CAST") + result = ois.readObject() as ArrayList + ois.close() + fis.close() + Log.d(TAG, "Got history of ${result.size} calls") + file.delete() + } catch (e: Exception) { + Log.e(TAG, "InputStream exception: - $e") + } + } + return result + } + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/tutpro/baresip/CallListAdapter.kt b/app/src/main/kotlin/com/tutpro/baresip/CallListAdapter.kt index 00bc491c..ba0d5d4e 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/CallListAdapter.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/CallListAdapter.kt @@ -84,7 +84,7 @@ class CallListAdapter(private val cxt: Context, private val rows: ArrayList() val indexes = ArrayList() @@ -10,4 +9,5 @@ class CallRow(val aor: String, val peerUri: String, direction: Int, val time: St directions.add(direction) indexes.add(index) } + } diff --git a/app/src/main/kotlin/com/tutpro/baresip/CallsActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/CallsActivity.kt index 11644bf7..cd5704b1 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/CallsActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/CallsActivity.kt @@ -16,8 +16,8 @@ import android.widget.TextView import androidx.activity.result.contract.ActivityResultContracts import com.tutpro.baresip.databinding.ActivityCallsBinding -import java.util.ArrayList import java.text.DateFormat +import java.util.* class CallsActivity : AppCompatActivity() { @@ -25,7 +25,6 @@ class CallsActivity : AppCompatActivity() { private lateinit var account: Account private lateinit var clAdapter: CallListAdapter - private var uaHistory = ArrayList() private var aor = "" private var lastClick: Long = 0 @@ -111,7 +110,7 @@ class CallsActivity : AppCompatActivity() { } DialogInterface.BUTTON_POSITIVE -> { removeUaHistoryAt(pos) - CallHistory.save() + NewCallHistory.save() clAdapter.notifyDataSetChanged() } DialogInterface.BUTTON_NEUTRAL -> { @@ -162,8 +161,8 @@ class CallsActivity : AppCompatActivity() { setMessage(String.format(getString(R.string.delete_history_alert), aor.substringAfter(":"))) setPositiveButton(getText(R.string.delete)) { dialog, _ -> - CallHistory.clear(aor) - CallHistory.save() + NewCallHistory.clear(aor) + NewCallHistory.save() aorGenerateHistory(aor) clAdapter.notifyDataSetChanged() dialog.dismiss() @@ -218,10 +217,8 @@ class CallsActivity : AppCompatActivity() { } override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.calls_menu, menu) return true - } private fun aorGenerateHistory(aor: String) { @@ -230,12 +227,12 @@ class CallsActivity : AppCompatActivity() { val h = BaresipService.callHistory[i] if (h.aor == aor) { val direction: Int = if (h.direction == "in") - if (h.connected) + if (h.startTime != null) R.drawable.arrow_down_green else R.drawable.arrow_down_red else - if (h.connected) + if (h.startTime != null) R.drawable.arrow_up_green else R.drawable.arrow_up_red @@ -243,11 +240,21 @@ class CallsActivity : AppCompatActivity() { uaHistory.last().directions.add(direction) uaHistory.last().indexes.add(i) } else { - val fmt: DateFormat = if (isToday(h.time.timeInMillis)) - DateFormat.getTimeInstance(DateFormat.SHORT) - else - DateFormat.getDateInstance(DateFormat.SHORT) - val time = fmt.format(h.time.time) + var time: String + if (isToday(h.stopTime.timeInMillis)) { + val fmt = DateFormat.getTimeInstance(DateFormat.SHORT) + time = getString(R.string.today) + "\n" + fmt.format(h.stopTime.time) + } else { + val month = h.stopTime.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.getDefault()) + val day = h.stopTime.get(Calendar.DAY_OF_MONTH) + val currentYear = Calendar.getInstance().get(Calendar.YEAR) + if (h.stopTime.get(Calendar.YEAR) == currentYear) { + val fmt = DateFormat.getTimeInstance(DateFormat.SHORT) + time = "$month $day" + "\n" + fmt.format(h.stopTime.time) + } else { + time = "$month $day" + "\n" + h.stopTime.get(Calendar.YEAR) + } + } uaHistory.add(CallRow(h.aor, h.peerUri, direction, time, i)) } } @@ -260,4 +267,8 @@ class CallsActivity : AppCompatActivity() { uaHistory.removeAt(i) } + companion object { + var uaHistory = ArrayList() + } + } diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt index b7eebc8a..1c334aeb 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt @@ -357,10 +357,12 @@ class MainActivity : AppCompatActivity() { rejectButton.setOnClickListener { val ua = UserAgent.uas()[aorSpinner.selectedItemPosition] val aor = ua.account.aor - val callp = Call.uaCalls(ua, "in")[0].callp + val call = Call.uaCalls(ua, "in")[0] + val callp = call.callp Log.d(TAG, "AoR $aor rejecting call $callp from ${callUri.text}") answerButton.isEnabled = false rejectButton.isEnabled = false + call.rejected = true Api.ua_hangup(ua.uap, callp, 486, "Rejected") } @@ -1677,7 +1679,7 @@ class MainActivity : AppCompatActivity() { } } } else { - val latest = CallHistory.aorLatestHistory(aor) + val latest = NewCallHistory.aorLatestHistory(aor) if (latest != null) callUri.setText( Utils.friendlyUri( diff --git a/app/src/main/res/layout/call_row.xml b/app/src/main/res/layout/call_row.xml index 19d97390..7d57db56 100644 --- a/app/src/main/res/layout/call_row.xml +++ b/app/src/main/res/layout/call_row.xml @@ -63,7 +63,8 @@ android:paddingStart="0dp" android:paddingEnd="5dp" android:textSize="12sp" - android:maxLines="1" + android:lines="2" + android:maxLines="2" android:text="" >