Added initial support for attended call transfer

Use red colored spinner text when account has active call
This commit is contained in:
Juha Heinanen
2022-03-11 11:35:05 +02:00
parent 05f601a4dd
commit 6406f15617
10 changed files with 231 additions and 60 deletions
+15
View File
@@ -1277,6 +1277,21 @@ Java_com_tutpro_baresip_Call_call_1has_1video(JNIEnv *env, jobject thiz, jlong c
return call_has_video((struct call *)call) ? true : false; return call_has_video((struct call *)call) ? true : false;
} }
JNIEXPORT jboolean JNICALL
Java_com_tutpro_baresip_Call_call_1replaces(JNIEnv *env, jobject thiz, jlong call)
{
return call_supported((struct call *)call, REPLACES) ? true : false;
}
JNIEXPORT jboolean JNICALL
Java_com_tutpro_baresip_Call_call_1replace_1transfer(JNIEnv *env, jobject thiz, jlong xferCall, jlong call)
{
re_thread_enter();
int res = call_replace_transfer((struct call *)xferCall, (struct call *)call);
re_thread_leave();
return res == 0 ? true : false;
}
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_Api_message_1send(JNIEnv *env, jobject thiz, jlong ua, jstring jPeer, jstring jMsg, jstring jTime) Java_com_tutpro_baresip_Api_message_1send(JNIEnv *env, jobject thiz, jlong ua, jstring jPeer, jstring jMsg, jstring jTime)
{ {
@@ -838,13 +838,18 @@ class BaresipService: Service() {
Log.d(TAG, "AoR $aor call $callp is closed") Log.d(TAG, "AoR $aor call $callp is closed")
stopRinging() stopRinging()
stopMediaPlayer() stopMediaPlayer()
/* when (ev[2]) { val newCall = call!!.newCall
"busy" -> if (newCall != null) {
playMedia(R.raw.busy, 1) newCall.onHoldCall = null
"error" -> call.newCall = null
playMedia(R.raw.error, 1) }
} */ val onHoldCall = call.onHoldCall
call!!.remove() if (onHoldCall != null) {
onHoldCall.newCall = null
onHoldCall.referTo = ""
call.onHoldCall = null
}
call.remove()
if (Call.calls().size == 0) { if (Call.calls().size == 0) {
resetCallVolume() resetCallVolume()
am.isSpeakerphoneOn = false am.isSpeakerphoneOn = false
@@ -911,7 +916,7 @@ class BaresipService: Service() {
return return
} }
val reason = ev[1].trim() val reason = ev[1].trim()
if ((reason != "") && (Call.uaCalls(ua, "").size == 0)) { if ((reason != "") && (ua.calls().isEmpty())) {
if (reason[0].isDigit()) if (reason[0].isDigit())
toast("${getString(R.string.call_failed)}: $reason") toast("${getString(R.string.call_failed)}: $reason")
else else
+26 -25
View File
@@ -8,6 +8,8 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str
var onhold = false var onhold = false
var held = false var held = false
var onHoldCall: Call? = null
var newCall: Call? = null
var rejected = false var rejected = false
var security = 0 var security = 0
var zid = "" var zid = ""
@@ -15,31 +17,39 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str
var referTo = "" var referTo = ""
fun add() { fun add() {
BaresipService.calls.add(this) BaresipService.calls.add(0, this)
} }
fun remove() { fun remove() {
BaresipService.calls.remove(this) BaresipService.calls.remove(this)
} }
fun connect(uri: String): Int { fun connect(uri: String): Boolean {
return call_connect(callp, uri) return call_connect(callp, uri) == 0
} }
fun hold(): Int { fun hold(): Boolean {
return call_hold(callp, true) return call_hold(callp, true) == 0
} }
fun resume(): Int { fun resume(): Boolean {
return call_hold(callp, false) return call_hold(callp, false) == 0
} }
fun transfer(uri: String): Int { fun transfer(uri: String): Boolean {
val err = call_hold(callp, true) val err = call_hold(callp, true)
if (err != 0) if (err != 0)
return err return false
referTo = uri referTo = uri
return call_transfer(callp, uri) return call_transfer(callp, uri) == 0
}
fun executeTransfer(): Boolean {
return if (this.onHoldCall != null) {
call_hold(callp, true)
call_replace_transfer(this.onHoldCall!!.callp, callp)
} else
false
} }
fun sendDigit(digit: Char): Int { fun sendDigit(digit: Char): Int {
@@ -54,7 +64,6 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str
return call_duration(callp) return call_duration(callp)
} }
fun stats(stream: String): String { fun stats(stream: String): String {
return call_stats(callp, stream) return call_stats(callp, stream)
} }
@@ -63,6 +72,10 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str
return call_audio_codecs(callp) return call_audio_codecs(callp)
} }
fun replaces(): Boolean {
return call_replaces(callp)
}
init { init {
if (ua.account.mediaEnc != "") security = R.drawable.box_red if (ua.account.mediaEnc != "") security = R.drawable.box_red
} }
@@ -78,6 +91,8 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str
private external fun call_duration(callp: Long): Int private external fun call_duration(callp: Long): Int
private external fun call_stats(callp: Long, stream: String): String private external fun call_stats(callp: Long, stream: String): String
private external fun call_has_video(callp: Long): Boolean private external fun call_has_video(callp: Long): Boolean
private external fun call_replaces(callp: Long): Boolean
private external fun call_replace_transfer(xfer_callp: Long, callp: Long): Boolean
companion object { companion object {
@@ -85,13 +100,6 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str
return BaresipService.calls return BaresipService.calls
} }
fun uaCalls(ua: UserAgent, dir: String): ArrayList<Call> {
val result = ArrayList<Call>()
for (c in BaresipService.calls)
if ((c.ua == ua) && ((dir == "") || c.dir == dir)) result.add(c)
return result
}
fun ofCallp(callp: Long): Call? { fun ofCallp(callp: Long): Call? {
for (c in BaresipService.calls) for (c in BaresipService.calls)
if (c.callp == callp) return c if (c.callp == callp) return c
@@ -104,12 +112,5 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str
return null return null
} }
fun isStatus(status: String): Boolean {
for (call in BaresipService.calls)
if (call.status == status)
return true
return false
}
} }
} }
@@ -3,6 +3,7 @@ package com.tutpro.baresip
import android.Manifest import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.app.AlertDialog
import android.app.KeyguardManager import android.app.KeyguardManager
import android.app.NotificationManager import android.app.NotificationManager
import android.content.* import android.content.*
@@ -75,6 +76,7 @@ class MainActivity : AppCompatActivity() {
private var speakerIcon: MenuItem? = null private var speakerIcon: MenuItem? = null
private lateinit var swipeRefresh: SwipeRefreshLayout private lateinit var swipeRefresh: SwipeRefreshLayout
private lateinit var requestPermissionLauncher: ActivityResultLauncher<String> private lateinit var requestPermissionLauncher: ActivityResultLauncher<String>
private var executeTransferDialog: androidx.appcompat.app.AlertDialog? = null
private lateinit var accountsRequest: ActivityResultLauncher<Intent> private lateinit var accountsRequest: ActivityResultLauncher<Intent>
private lateinit var chatRequests: ActivityResultLauncher<Intent> private lateinit var chatRequests: ActivityResultLauncher<Intent>
@@ -297,7 +299,8 @@ class MainActivity : AppCompatActivity() {
setTitle(R.string.info) setTitle(R.string.info)
setMessage(getString(R.string.call_is_secure)) setMessage(getString(R.string.call_is_secure))
setPositiveButton(getString(R.string.unverify)) { dialog, _ -> setPositiveButton(getString(R.string.unverify)) { dialog, _ ->
val calls = Call.uaCalls(UserAgent.uas()[aorSpinner.selectedItemPosition], "") val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
val calls = ua.calls()
if (calls.size > 0) { if (calls.size > 0) {
if (Api.cmd_exec("zrtp_unverify " + calls[0].zid) != 0) { if (Api.cmd_exec("zrtp_unverify " + calls[0].zid) != 0) {
Log.e(TAG, "Command 'zrtp_unverify ${calls[0].zid}' failed") Log.e(TAG, "Command 'zrtp_unverify ${calls[0].zid}' failed")
@@ -329,7 +332,7 @@ class MainActivity : AppCompatActivity() {
hangupButton.setOnClickListener { hangupButton.setOnClickListener {
val ua = UserAgent.uas()[aorSpinner.selectedItemPosition] val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
val aor = ua.account.aor val aor = ua.account.aor
val uaCalls = Call.uaCalls(ua, "") val uaCalls = ua.calls()
if (uaCalls.size > 0) { if (uaCalls.size > 0) {
val call = uaCalls[uaCalls.size - 1] val call = uaCalls[uaCalls.size - 1]
val callp = call.callp val callp = call.callp
@@ -344,7 +347,7 @@ class MainActivity : AppCompatActivity() {
answerButton.setOnClickListener { answerButton.setOnClickListener {
val ua = UserAgent.uas()[aorSpinner.selectedItemPosition] val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
val aor = ua.account.aor val aor = ua.account.aor
val callp = Call.uaCalls(ua, "in")[0].callp val callp = ua.calls("in")[0].callp
Log.d(TAG, "AoR $aor answering call $callp from ${callUri.text}") Log.d(TAG, "AoR $aor answering call $callp from ${callUri.text}")
answerButton.isEnabled = false answerButton.isEnabled = false
rejectButton.isEnabled = false rejectButton.isEnabled = false
@@ -359,7 +362,7 @@ class MainActivity : AppCompatActivity() {
rejectButton.setOnClickListener { rejectButton.setOnClickListener {
val ua = UserAgent.uas()[aorSpinner.selectedItemPosition] val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
val aor = ua.account.aor val aor = ua.account.aor
val call = Call.uaCalls(ua, "in")[0] val call = ua.calls("in")[0]
val callp = call.callp val callp = call.callp
Log.d(TAG, "AoR $aor rejecting call $callp from ${callUri.text}") Log.d(TAG, "AoR $aor rejecting call $callp from ${callUri.text}")
answerButton.isEnabled = false answerButton.isEnabled = false
@@ -371,7 +374,7 @@ class MainActivity : AppCompatActivity() {
holdButton.setOnClickListener { holdButton.setOnClickListener {
val ua = UserAgent.uas()[aorSpinner.selectedItemPosition] val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
val aor = ua.account.aor val aor = ua.account.aor
val call = Call.uaCalls(ua, "")[0] val call = ua.calls()[0]
if (call.onhold) { if (call.onhold) {
Log.d(TAG, "AoR $aor resuming call ${call.callp} with ${callUri.text}") Log.d(TAG, "AoR $aor resuming call ${call.callp} with ${callUri.text}")
call.resume() call.resume()
@@ -391,7 +394,7 @@ class MainActivity : AppCompatActivity() {
infoButton.setOnClickListener { infoButton.setOnClickListener {
val ua = UserAgent.uas()[aorSpinner.selectedItemPosition] val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
val calls = Call.uaCalls(ua, "") val calls = ua.calls()
if (calls.size > 0) { if (calls.size > 0) {
val call = calls[0] val call = calls[0]
val stats = call.stats("audio") val stats = call.stats("audio")
@@ -982,6 +985,7 @@ class MainActivity : AppCompatActivity() {
"call incoming", "call outgoing" -> { "call incoming", "call outgoing" -> {
val callp = params[1] as Long val callp = params[1] as Long
if (BaresipService.isMainVisible) { if (BaresipService.isMainVisible) {
uaAdapter.notifyDataSetChanged()
if (aor != aorSpinner.tag) if (aor != aorSpinner.tag)
spinToAor(aor) spinToAor(aor)
showCall(ua, Call.ofCallp(callp)) showCall(ua, Call.ofCallp(callp))
@@ -1110,6 +1114,14 @@ class MainActivity : AppCompatActivity() {
showCall(ua) showCall(ua)
} }
"call closed" -> { "call closed" -> {
uaAdapter.notifyDataSetChanged()
val call = ua.currentCall()
if (call != null) {
call.resume()
startCallTimer(call)
} else {
callTimer.stop()
}
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
@@ -1122,7 +1134,6 @@ class MainActivity : AppCompatActivity() {
} }
if (speakerIcon != null) if (speakerIcon != null)
speakerIcon!!.setIcon(R.drawable.speaker_off) speakerIcon!!.setIcon(R.drawable.speaker_off)
callTimer.stop()
if ((Build.VERSION.SDK_INT >= 22 && kgm.isDeviceLocked) || if ((Build.VERSION.SDK_INT >= 22 && kgm.isDeviceLocked) ||
(Build.VERSION.SDK_INT < 22 && kgm.isKeyguardLocked && kgm.isKeyguardSecure)) (Build.VERSION.SDK_INT < 22 && kgm.isKeyguardLocked && kgm.isKeyguardSecure))
Utils.setShowWhenLocked(this, false) Utils.setShowWhenLocked(this, false)
@@ -1371,14 +1382,18 @@ class MainActivity : AppCompatActivity() {
} }
private fun makeTransfer(ua: UserAgent) { private fun makeTransfer(ua: UserAgent) {
val layout = LayoutInflater.from(this) val layout = LayoutInflater.from(this)
.inflate(R.layout.call_transfer_dialog, findViewById(android.R.id.content), .inflate(R.layout.call_transfer_dialog, findViewById(android.R.id.content), false)
false) val blind = layout.findViewById(R.id.blind) as CheckBox
val attended = layout.findViewById(R.id.attended) as CheckBox
val transferUri = layout.findViewById(R.id.transferUri) as AutoCompleteTextView val transferUri = layout.findViewById(R.id.transferUri) as AutoCompleteTextView
transferUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item, transferUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item,
Contact.contactNames())) Contact.contactNames()))
transferUri.threshold = 2 transferUri.threshold = 2
transferUri.requestFocus() transferUri.requestFocus()
val builder = MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme) val builder = MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)
with(builder) { with(builder) {
setView(layout) setView(layout)
@@ -1398,8 +1413,19 @@ class MainActivity : AppCompatActivity() {
Utils.alertView(this@MainActivity, getString(R.string.notice), Utils.alertView(this@MainActivity, getString(R.string.notice),
String.format(getString(R.string.invalid_sip_or_tel_uri), uri)) String.format(getString(R.string.invalid_sip_or_tel_uri), uri))
} else { } else {
if (Call.uaCalls(ua, "").size > 0) { val call = ua.currentCall()
Call.uaCalls(ua, "")[0].transfer(uri) if (call != null) {
if (attended.isChecked) {
if (call.hold()) {
call.referTo = uri
call(ua, uri, call)
}
} else {
if (!call.transfer(uri)) {
Utils.alertView(this@MainActivity, getString(R.string.notice),
String.format(getString(R.string.transfer_failed)))
}
}
showCall(ua) showCall(ua)
} }
} }
@@ -1411,6 +1437,27 @@ class MainActivity : AppCompatActivity() {
} }
} }
val alertDialog = builder.create() val alertDialog = builder.create()
val call = ua.currentCall() ?: return
val blindOrAttended = layout.findViewById(R.id.blindOrAttended) as RelativeLayout
if (call.replaces()) {
blind.setOnClickListener {
if (blind.isChecked) {
attended.isChecked = false
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).text = getString(R.string.transfer)
}
}
attended.setOnClickListener {
if (attended.isChecked) {
blind.isChecked = false
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).text = getString(R.string.call)
}
}
blindOrAttended.visibility = View.VISIBLE
} else {
blindOrAttended.visibility = View.GONE
}
// This needs to be done after dialog has been created and before it is shown // This needs to be done after dialog has been created and before it is shown
alertDialog.window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE) alertDialog.window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
alertDialog.show() alertDialog.show()
@@ -1608,20 +1655,23 @@ class MainActivity : AppCompatActivity() {
} }
private fun call(ua: UserAgent, uri: String): Boolean { private fun call(ua: UserAgent, uri: String, onHoldCall: Call? = null): Boolean {
if (ua.account.aor != aorSpinner.tag) if (ua.account.aor != aorSpinner.tag)
spinToAor(ua.account.aor) spinToAor(ua.account.aor)
val callp = Api.ua_call_alloc(ua.uap, 0L, Api.VIDMODE_OFF) val callp = Api.ua_call_alloc(ua.uap, 0L, Api.VIDMODE_OFF)
return if (callp != 0L) { return if (callp != 0L) {
Log.d(TAG, "Adding outgoing call ${ua.uap}/$callp/$uri") Log.d(TAG, "Adding outgoing call ${ua.uap}/$callp/$uri")
val call = Call(callp, ua, uri, "out", "outgoing", Utils.dtmfWatcher(callp)) val call = Call(callp, ua, uri, "out", "outgoing", Utils.dtmfWatcher(callp))
call.onHoldCall = onHoldCall
call.add() call.add()
val err = call.connect(uri) if (onHoldCall != null)
if (err == 0) { onHoldCall.newCall = call
if (call.connect(uri)) {
showCall(ua) showCall(ua)
true true
} else { } else {
Log.w(TAG, "call_connect $callp failed with error $err") showCall(ua)
Log.w(TAG, "call_connect $callp failed")
false false
} }
} else { } else {
@@ -1637,13 +1687,12 @@ class MainActivity : AppCompatActivity() {
val newCall = Call(newCallp, ua, uri, "out", "transferring", val newCall = Call(newCallp, ua, uri, "out", "transferring",
Utils.dtmfWatcher(newCallp)) Utils.dtmfWatcher(newCallp))
newCall.add() newCall.add()
val err = newCall.connect(uri) if (newCall.connect(uri)) {
if (err == 0) {
if (ua.account.aor != aorSpinner.tag) if (ua.account.aor != aorSpinner.tag)
spinToAor(ua.account.aor) spinToAor(ua.account.aor)
showCall(ua) showCall(ua)
} else { } else {
Log.w(TAG, "call_connect $newCallp failed with error $err") Log.w(TAG, "call_connect $newCallp failed")
call.notifySipfrag(500, "Call Error") call.notifySipfrag(500, "Call Error")
} }
} else { } else {
@@ -1712,6 +1761,7 @@ class MainActivity : AppCompatActivity() {
hangupButton.visibility = View.INVISIBLE hangupButton.visibility = View.INVISIBLE
hangupButton.isEnabled = false hangupButton.isEnabled = false
} else { } else {
uaAdapter.notifyDataSetChanged()
callButton.visibility = View.INVISIBLE callButton.visibility = View.INVISIBLE
callButton.isEnabled = false callButton.isEnabled = false
hangupButton.visibility = View.VISIBLE hangupButton.visibility = View.VISIBLE
@@ -1728,7 +1778,8 @@ class MainActivity : AppCompatActivity() {
} }
private fun showCall(ua: UserAgent, showCall: Call? = null) { private fun showCall(ua: UserAgent, showCall: Call? = null) {
if (Call.uaCalls(ua, "").size == 0) { val call = showCall ?: ua.currentCall()
if (call == null) {
swipeRefresh.isEnabled = true swipeRefresh.isEnabled = true
callTitle.text = getString(R.string.outgoing_call_to_dots) callTitle.text = getString(R.string.outgoing_call_to_dots)
callTimer.visibility = View.INVISIBLE callTimer.visibility = View.INVISIBLE
@@ -1757,7 +1808,6 @@ class MainActivity : AppCompatActivity() {
onHoldNotice.visibility = View.GONE onHoldNotice.visibility = View.GONE
} else { } else {
swipeRefresh.isEnabled = false swipeRefresh.isEnabled = false
val call = showCall ?: Call.uaCalls(ua, "")[0]
callUri.isFocusable = false callUri.isFocusable = false
when (call.status) { when (call.status) {
"outgoing", "transferring", "answered" -> { "outgoing", "transferring", "answered" -> {
@@ -1813,9 +1863,26 @@ class MainActivity : AppCompatActivity() {
Utils.aorDomain(ua.account.aor))) Utils.aorDomain(ua.account.aor)))
transferButton.isEnabled = true transferButton.isEnabled = true
} }
callTimer.base = SystemClock.elapsedRealtime() - (call.duration() * 1000L) if (call.onHoldCall != null) {
callTimer.stop() val builder = MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)
callTimer.start() with(builder) {
setTitle(getString(R.string.execute_transfer))
setPositiveButton(getText(R.string.yes)) { dialog, _ ->
dialog.dismiss()
call.executeTransfer()
}
setNeutralButton(getText(R.string.no)) { dialog, _ ->
dialog.dismiss()
Api.ua_hangup(ua.uap, call.callp, 0, "")
}
}
executeTransferDialog = builder.create()
executeTransferDialog!!.show()
} else {
executeTransferDialog?.dismiss()
executeTransferDialog = null
}
startCallTimer(call)
callTimer.visibility = View.VISIBLE callTimer.visibility = View.VISIBLE
if (ua.account.mediaEnc == "") { if (ua.account.mediaEnc == "") {
securityButton.visibility = View.INVISIBLE securityButton.visibility = View.INVISIBLE
@@ -1981,13 +2048,19 @@ class MainActivity : AppCompatActivity() {
private fun saveCallUri() { private fun saveCallUri() {
if (UserAgent.uas().isNotEmpty() && aorSpinner.selectedItemPosition >= 0) { if (UserAgent.uas().isNotEmpty() && aorSpinner.selectedItemPosition >= 0) {
val ua = UserAgent.uas()[aorSpinner.selectedItemPosition] val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
if (Call.uaCalls(ua, "").size == 0) if (ua.calls().isEmpty())
ua.account.resumeUri = callUri.text.toString() ua.account.resumeUri = callUri.text.toString()
else else
ua.account.resumeUri = "" ua.account.resumeUri = ""
} }
} }
private fun startCallTimer(call: Call) {
callTimer.stop()
callTimer.base = SystemClock.elapsedRealtime() - (call.duration() * 1000L)
callTimer.start()
}
companion object { companion object {
var accountRequest: ActivityResultLauncher<Intent>? = null var accountRequest: ActivityResultLauncher<Intent>? = null
@@ -7,10 +7,11 @@ import android.view.ViewGroup
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat.getColor
import java.util.ArrayList import java.util.ArrayList
class UaSpinnerAdapter(cxt: Context, private val uas: ArrayList<UserAgent>, class UaSpinnerAdapter(private val cxt: Context, private val uas: ArrayList<UserAgent>,
private val images: ArrayList<Int>) : private val images: ArrayList<Int>) :
ArrayAdapter<Int>(cxt, android.R.layout.simple_spinner_item, images) { ArrayAdapter<Int>(cxt, android.R.layout.simple_spinner_item, images) {
@@ -43,8 +44,11 @@ class UaSpinnerAdapter(cxt: Context, private val uas: ArrayList<UserAgent>,
viewHolder = rowView.tag as ViewHolder viewHolder = rowView.tag as ViewHolder
} }
viewHolder.textView.text = uas[position].account.aor.split(":")[1] val ua = uas[position]
viewHolder.textView.text = ua.account.aor.split(":")[1]
viewHolder.textView.textSize = 17f viewHolder.textView.textSize = 17f
if (ua.calls().isNotEmpty())
viewHolder.textView.setTextColor(getColor(cxt, R.color.colorRed))
viewHolder.imageView.setImageResource(images[position]) viewHolder.imageView.setImageResource(images[position])
return rowView return rowView
@@ -19,6 +19,20 @@ class UserAgent(val uap: Long) {
BaresipService.status[BaresipService.uas.indexOf(this)] = status BaresipService.status[BaresipService.uas.indexOf(this)] = status
} }
fun calls(dir: String = ""): ArrayList<Call> {
val result = ArrayList<Call>()
for (c in BaresipService.calls)
if ((c.ua == this) && ((dir == "") || c.dir == dir)) result.add(c)
return result
}
fun currentCall(): Call? {
for (c in BaresipService.calls)
if (c.ua == this)
return c
return null
}
companion object { companion object {
fun uas(): ArrayList<UserAgent> { fun uas(): ArrayList<UserAgent> {
@@ -123,6 +123,7 @@ object Utils {
val params = uriParams(u) val params = uriParams(u)
if (uri.startsWith("<") && (uri.endsWith(">"))) if (uri.startsWith("<") && (uri.endsWith(">")))
u = uri.substring(1).substringBeforeLast(">") u = uri.substring(1).substringBeforeLast(">")
u = u.substringBefore("?")
u = u.replace(":5060", "") u = u.replace(":5060", "")
u = u.replace(";transport=udp", "", true) u = u.replace(";transport=udp", "", true)
if (u.split(":").size == 3 || if (u.split(":").size == 3 ||
@@ -11,15 +11,67 @@
android:paddingTop="?dialogPreferredPadding" android:paddingTop="?dialogPreferredPadding"
android:paddingStart="?dialogPreferredPadding" android:paddingStart="?dialogPreferredPadding"
android:paddingEnd="?dialogPreferredPadding" android:paddingEnd="?dialogPreferredPadding"
android:paddingBottom="8dp"
android:textSize="20sp" android:textSize="20sp"
android:text="@string/call_transfer" android:text="@string/call_transfer"
android:textColor="@color/colorPrimary" /> android:textColor="@color/colorPrimary" />
<RelativeLayout
android:id="@+id/blindOrAttended"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="?dialogPreferredPadding"
android:paddingEnd="?dialogPreferredPadding"
android:visibility="gone"
android:orientation="horizontal" >
<TextView
android:id="@+id/blindTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:textSize="16sp"
android:text="@string/blind" >
</TextView>
<CheckBox
android:id="@+id/blind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/blindTitle"
android:checked="true" >
</CheckBox>
<TextView
android:id="@+id/attendedTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toStartOf="@id/attended"
android:textSize="16sp"
android:text="@string/attended" >
</TextView>
<CheckBox
android:id="@+id/attended"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:checked="false" >
</CheckBox>
</RelativeLayout>
<TextView <TextView
android:id="@+id/transferDestination" android:id="@+id/transferDestination"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="?dialogPreferredPadding" android:paddingEnd="?dialogPreferredPadding"
android:paddingStart="?dialogPreferredPadding"
android:paddingBottom="12dp"
android:textSize="16sp" android:textSize="16sp"
android:text="@string/transfer_destination" android:text="@string/transfer_destination"
android:textColor="@color/colorStrong" /> android:textColor="@color/colorStrong" />
+3
View File
@@ -452,8 +452,11 @@
<string name="call_is_on_hold">Puhelu on pidossa</string> <string name="call_is_on_hold">Puhelu on pidossa</string>
<string name="mic">Mikrofoni päälle/pois</string> <string name="mic">Mikrofoni päälle/pois</string>
<string name="call_transfer">Puhelun siirto</string> <string name="call_transfer">Puhelun siirto</string>
<string name="blind">Sokeasti</string>
<string name="attended">Osallistu</string>
<string name="transfer_destination">Siirron kohde</string> <string name="transfer_destination">Siirron kohde</string>
<string name="transfer">Siirto</string> <string name="transfer">Siirto</string>
<string name="execute_transfer">Suorita siirto</string>
<string name="transfer_failed">Siirto epäonnistui</string> <string name="transfer_failed">Siirto epäonnistui</string>
<string name="dtmf">DTMF</string> <string name="dtmf">DTMF</string>
<string name="call_info">Puhelutiedot</string> <string name="call_info">Puhelutiedot</string>
+3
View File
@@ -412,8 +412,11 @@
<string name="call_is_on_hold">Call is on hold</string> <string name="call_is_on_hold">Call is on hold</string>
<string name="mic">Microphone On/Off</string> <string name="mic">Microphone On/Off</string>
<string name="call_transfer">Call Transfer</string> <string name="call_transfer">Call Transfer</string>
<string name="blind">Blind</string>
<string name="attended">Attended</string>
<string name="transfer_destination">Transfer destination</string> <string name="transfer_destination">Transfer destination</string>
<string name="transfer">Transfer</string> <string name="transfer">Transfer</string>
<string name="execute_transfer">Execute Transfer</string>
<string name="transfer_failed">Transfer Failed</string> <string name="transfer_failed">Transfer Failed</string>
<string name="dtmf">DTMF</string> <string name="dtmf">DTMF</string>
<string name="call_info">Call Info</string> <string name="call_info">Call Info</string>