diff --git a/app/build.gradle b/app/build.gradle index 541e6c75..19fe8acd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId = 'com.tutpro.baresip' minSdkVersion 21 targetSdkVersion 26 - versionCode = 18 - versionName = '2.3.2' + versionCode = 19 + versionName = '2.4.0' externalNativeBuild { cmake { cFlags '-DHAVE_INTTYPES_H' diff --git a/app/src/main/cpp/baresip.c b/app/src/main/cpp/baresip.c index a26331bf..b96c6bd9 100644 --- a/app/src/main/cpp/baresip.c +++ b/app/src/main/cpp/baresip.c @@ -801,7 +801,6 @@ JNIEXPORT void JNICALL Java_com_tutpro_baresip_MainActivity_uag_1current_1set(JNIEnv *env, jobject thiz, jstring javaUA) { - struct ua *new_current_ua; const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0); struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10); (*env)->ReleaseStringUTFChars(env, javaUA, native_ua); @@ -810,18 +809,6 @@ Java_com_tutpro_baresip_MainActivity_uag_1current_1set(JNIEnv *env, jobject thiz return; } -JNIEXPORT jstring JNICALL -Java_com_tutpro_baresip_MainActivity_uag_1current(JNIEnv *env, jobject thiz) -{ - struct ua *current_ua = uag_current(); - char ua_buf[256]; - if (current_ua == NULL) - ua_buf[0] = '\0'; - else - sprintf(ua_buf, "%lu", (unsigned long)current_ua); - return (*env)->NewStringUTF(env, ua_buf); -} - JNIEXPORT jstring JNICALL Java_com_tutpro_baresip_Api_call_1peeruri(JNIEnv *env, jobject thiz, jstring javaCall) { diff --git a/app/src/main/kotlin/com/tutpro/baresip/Call.kt b/app/src/main/kotlin/com/tutpro/baresip/Call.kt index 42ca3b30..95172400 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Call.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Call.kt @@ -6,7 +6,7 @@ import java.util.ArrayList class Call(val callp: String, val ua: UserAgent, val peerURI: String, val dir: String, var status: String, val dtmfWatcher: TextWatcher?) { - var hold = false + var onhold = false var security = 0 var zid = "" var hasHistory = false @@ -23,9 +23,8 @@ class Call(val callp: String, val ua: UserAgent, val peerURI: String, val dir: S fun uaCalls(calls: ArrayList, ua: UserAgent, dir: String): ArrayList { val result = ArrayList() - for (i in calls.indices) { - if ((calls[i].ua == ua) && (calls[i].dir == dir)) result.add(calls[i]) - } + for (i in calls.indices) + if ((calls[i].ua == ua) && ((dir == "") || calls[i].dir == dir)) result.add(calls[i]) return result } @@ -36,9 +35,5 @@ class Call(val callp: String, val ua: UserAgent, val peerURI: String, val dir: S return null } - fun uaCallIndex(calls: ArrayList, ua: UserAgent, call: Call, dir: String): Int { - return uaCalls(calls, ua, dir).indexOf(call) - } - } } diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt index 5dad6644..fb158051 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt @@ -38,9 +38,13 @@ class MainActivity : AppCompatActivity() { lateinit var appContext: Context internal lateinit var layout: RelativeLayout internal lateinit var baresipService: Intent - internal lateinit var callee: AutoCompleteTextView + internal lateinit var callTitle: TextView + internal lateinit var callUri: AutoCompleteTextView internal lateinit var securityButton: ImageButton internal lateinit var callButton: ImageButton + internal lateinit var hangupButton: ImageButton + internal lateinit var answerButton: ImageButton + internal lateinit var rejectButton: ImageButton internal lateinit var holdButton: ImageButton internal lateinit var contactsButton: ImageButton internal lateinit var messagesButton: ImageButton @@ -71,14 +75,18 @@ class MainActivity : AppCompatActivity() { appContext = applicationContext layout = findViewById(R.id.mainActivityLayout) as RelativeLayout - callee = findViewById(R.id.callee) as AutoCompleteTextView + callTitle = findViewById(R.id.callTitle) as TextView + callUri = findViewById(R.id.uri) as AutoCompleteTextView securityButton = findViewById(R.id.securityButton) as ImageButton callButton = findViewById(R.id.callButton) as ImageButton + hangupButton = findViewById(R.id.hangupButton) as ImageButton + answerButton = findViewById(R.id.answerButton) as ImageButton + rejectButton = findViewById(R.id.rejectButton) as ImageButton holdButton = findViewById(R.id.holdButton) as ImageButton + dtmf = findViewById(R.id.dtmf) as EditText contactsButton = findViewById(R.id.contactsButton) as ImageButton messagesButton = findViewById(R.id.messagesButton) as ImageButton callsButton = findViewById(R.id.callsButton) as ImageButton - dtmf = findViewById(R.id.dtmf) as EditText am = getSystemService(Context.AUDIO_SERVICE) as AudioManager val rtUri = RingtoneManager.getActualDefaultRingtoneUri(applicationContext, @@ -115,57 +123,79 @@ class MainActivity : AppCompatActivity() { Log.d("Baresip", "Setting $aor current") uag_current_set(uas[position].uap) val callsOut = Call.uaCalls(calls, ua, "out") - if (callsOut.size == 0) { - callee.text.clear() - callee.hint = "Callee" - callButton.tag = "Call" - callButton.setImageResource(R.drawable.call_green) + val callsIn = Call.uaCalls(calls, ua, "in") + if ((callsOut.size == 0) && (callsIn.size == 0)) { + callTitle.text = "Outgoing call to ..." + callUri.text.clear() + callUri.hint = "Callee" securityButton.visibility = View.INVISIBLE + callButton.visibility = View.VISIBLE + hangupButton.visibility = View.INVISIBLE + answerButton.visibility = View.INVISIBLE + rejectButton.visibility = View.INVISIBLE dtmf.visibility = View.INVISIBLE } else { - callee.setText(callsOut[0].peerURI) - callButton.tag = callsOut[0].status - if (callsOut[0].status == "Hangup") { - if (callsOut[0].hold) { - holdButton.setImageResource(R.drawable.play) - } else { - holdButton.setImageResource(R.drawable.pause) - } - holdButton.visibility = View.VISIBLE - securityButton.setImageResource(callsOut[0].security) - setSecurityButtonTag(securityButton, callsOut[0].security) - if ((acc.mediaenc == "zrtp") || (acc.mediaenc == "dtls_srtpf")) - securityButton.visibility = View.VISIBLE - else - securityButton.visibility = View.INVISIBLE - dtmf.visibility = View.VISIBLE - dtmf.requestFocus() + val call: Call + if (callsOut.size > 0) { + callTitle.text = "Outgoing call to ..." + call = callsOut[0] } else { - holdButton.visibility = View.INVISIBLE - securityButton.visibility = View.INVISIBLE - dtmf.visibility = View.INVISIBLE + callTitle.text = "Incoming call from ..." + call = callsIn[0] + } + callUri.setText(call.peerURI) + when (call.status) { + "outgoing" -> { + securityButton.visibility = View.INVISIBLE + callButton.visibility = View.INVISIBLE + hangupButton.visibility = View.VISIBLE + answerButton.visibility = View.INVISIBLE + rejectButton.visibility = View.INVISIBLE + holdButton.visibility = View.INVISIBLE + dtmf.visibility = View.INVISIBLE + } + "incoming" -> { + securityButton.visibility = View.INVISIBLE + callButton.visibility = View.INVISIBLE + hangupButton.visibility = View.INVISIBLE + answerButton.visibility = View.VISIBLE + answerButton.isEnabled = true + rejectButton.visibility = View.VISIBLE + rejectButton.isEnabled = true + holdButton.visibility = View.INVISIBLE + dtmf.visibility = View.INVISIBLE + if (answerCall == call.callp) { + answerCall = "" + answerButton.performClick() + } + if (rejectCall == call.callp) { + rejectCall = "" + rejectButton.performClick() + moveTaskToBack(true) + } + } + "connected" -> { + securityButton.setImageResource(call.security) + setSecurityButtonTag(securityButton, call.security) + if ((acc.mediaenc == "zrtp") || (acc.mediaenc == "dtls_srtpf")) + securityButton.visibility = View.VISIBLE + else + securityButton.visibility = View.INVISIBLE + callButton.visibility = View.INVISIBLE + hangupButton.visibility = View.VISIBLE + answerButton.visibility = View.INVISIBLE + rejectButton.visibility = View.INVISIBLE + if (call.onhold) { + holdButton.setImageResource(R.drawable.play) + } else { + holdButton.setImageResource(R.drawable.pause) + } + holdButton.visibility = View.VISIBLE + dtmf.visibility = View.VISIBLE + dtmf.requestFocus() + } } } - val view_count = layout.childCount - // Log.d("Baresip", "View count is $view_count") - if (view_count > 6) - layout.removeViews(6, view_count - 6) - for (c in Call.uaCalls(calls, ua, "in")) - for (call_index in Call.uaCalls(calls, ua, "in").indices) { - val startIndex = (call_index + 1) * 10 - addCallViews(ua, c, startIndex) - if (answerCall == c.callp) { - answerCall = "" - val answerButton = layout.findViewById(startIndex + 5) as ImageButton - answerButton.performClick() - } - if (rejectCall == c.callp) { - rejectCall = "" - val rejectButton = layout.findViewById(startIndex + 6) as ImageButton - rejectButton.performClick() - moveTaskToBack(true) - } - } } override fun onNothingSelected(parent: AdapterView<*>) { @@ -211,11 +241,11 @@ class MainActivity : AppCompatActivity() { unverifyDialog.setMessage("This call is SECURE and peer is VERIFIED! " + "Do you want to unverify the peer?") unverifyDialog.setPositiveButton("Unverify") { dialog, _ -> - val outCalls = Call.uaCalls(calls, uas[aorSpinner.selectedItemPosition], "out") - if (outCalls.size > 0) { - if (Api.cmd_exec("zrtp_unverify " + outCalls[0].zid) != 0) { + val calls = Call.uaCalls(calls, uas[aorSpinner.selectedItemPosition], "") + if (calls.size > 0) { + if (Api.cmd_exec("zrtp_unverify " + calls[0].zid) != 0) { Log.w("Baresip", - "Command 'zrtp_unverify ${outCalls[0].zid}' failed") + "Command 'zrtp_unverify ${calls[0].zid}' failed") } else { securityButton.setImageResource(R.drawable.box_yellow) securityButton.tag = "yellow" @@ -231,70 +261,81 @@ class MainActivity : AppCompatActivity() { } } - callButton.tag = "Call" callButton.setOnClickListener { val ua = uas[aorSpinner.selectedItemPosition] val aor = ua.account.aor - when (callButton.tag) { - "Call" -> { - if (!ONE_CALL_ONLY || calls.isEmpty()) { - val calleeText = (findViewById(R.id.callee) as EditText).text.toString() - .trim() - if (calleeText.length > 0) { - var uri = ContactsActivity.findContactURI(calleeText) - if (!uri.startsWith("sip:")) { - uri = "sip:$uri" - if (!uri.contains("@")) { - val host = aor.substring(aor.indexOf("@") + 1) - uri = "$uri@$host" - } - } - if (!Utils.checkSipUri(uri)) - Utils.alertView(this,"Notice", - "Invalid SIP URI '$uri'") - else - call(ua, uri) - } else { - val latest = CallHistory.aorLatestHistory(history, aor) - if (latest != null) - if (Utils.uriHostPart(latest.peerURI) == Utils.uriHostPart(aor)) - callee.setText(Utils.uriUserPart(latest.peerURI)) - else - callee.setText(latest.peerURI) + if (!ONE_CALL_ONLY || calls.isEmpty()) { + val uriText = (findViewById(R.id.uri) as EditText).text.toString().trim() + if (uriText.length > 0) { + var uri = ContactsActivity.findContactURI(uriText) + if (!uri.startsWith("sip:")) { + uri = "sip:$uri" + if (!uri.contains("@")) { + val host = aor.substring(aor.indexOf("@") + 1) + uri = "$uri@$host" } } - } - "Cancel" -> { - val callp = Call.calls(calls, "out")[0].callp - Log.d("Baresip", "Canceling AoR $aor call $callp to " + - (findViewById(R.id.callee) as EditText).text) - ua_hangup(ua.uap, callp, 486, "Rejected") - } - "Hangup" -> { - val callp = Call.calls(calls, "out")[0].callp - Log.d("Baresip", "Hanging up AoR $aor call $callp to " + - (findViewById(R.id.callee) as EditText).text) - callButton.isEnabled = false - ua_hangup(ua.uap, callp, 0, "") + if (!Utils.checkSipUri(uri)) + Utils.alertView(this,"Notice","Invalid SIP URI '$uri'") + else + call(ua, uri) + } else { + val latest = CallHistory.aorLatestHistory(history, aor) + if (latest != null) + if (Utils.uriHostPart(latest.peerURI) == Utils.uriHostPart(aor)) + callUri.setText(Utils.uriUserPart(latest.peerURI)) + else + callUri.setText(latest.peerURI) } } } - holdButton.tag = "Hold" - holdButton.visibility = View.INVISIBLE + hangupButton.setOnClickListener { + val ua = uas[aorSpinner.selectedItemPosition] + val aor = ua.account.aor + val callp = Call.uaCalls(calls, ua,"")[0].callp + Log.d("Baresip", "AoR $aor hanging up call $callp with " + + (findViewById(R.id.uri) as EditText).text) + hangupButton.isEnabled = false + ua_hangup(ua.uap, callp, 0, "") + } + + answerButton.setOnClickListener { + val ua = uas[aorSpinner.selectedItemPosition] + val aor = ua.account.aor + val callp = Call.uaCalls(calls, ua,"in")[0].callp + Log.d("Baresip", "AoR $aor answering call $callp from " + + (findViewById(R.id.uri) as EditText).text) + answerButton.isEnabled = false + ua_answer(ua.uap, callp) + } + + rejectButton.setOnClickListener { + val ua = uas[aorSpinner.selectedItemPosition] + val aor = ua.account.aor + val callp = Call.uaCalls(calls, ua,"in")[0].callp + Log.d("Baresip", "AoR $aor rejecting call $callp from " + + (findViewById(R.id.uri) as EditText).text) + rejectButton.isEnabled = false + ua_hangup(ua.uap, callp, 486, "Rejected") + } + holdButton.setOnClickListener { - when (holdButton.tag) { - "Hold" -> { - holdCallAt(Call.calls(calls, "out")[0], holdButton) - } - "Resume" -> { - Log.d("Baresip", "Resuming " + (findViewById(R.id.callee) as EditText).text) - val call = Call.calls(calls, "out")[0] - call_unhold(call.callp) - call.hold = false - holdButton.tag = "Hold" - holdButton.setImageResource(R.drawable.pause) - } + val ua = uas[aorSpinner.selectedItemPosition] + val aor = ua.account.aor + val call = Call.uaCalls(calls, ua,"")[0] + if (call.onhold) { + Log.d("Baresip", "AoR $aor resuming call ${call.callp} with " + + (findViewById(R.id.uri) as EditText).text) + call_unhold(call.callp) + call.onhold = false + holdButton.setImageResource(R.drawable.pause) + } else { + Log.d("Baresip", "AoR $aor holding call ${call.callp} with " + + (findViewById(R.id.uri) as EditText).text) + call_hold(call.callp) + call.onhold = true + holdButton.setImageResource(R.drawable.play) } } @@ -335,8 +376,8 @@ class MainActivity : AppCompatActivity() { startService(baresipService) } - callee.threshold = 2 - callee.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item, + callUri.threshold = 2 + callUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item, ContactsActivity.contacts.map { Contact -> Contact.name })) if (intent.hasExtra("onStartup")) @@ -390,7 +431,8 @@ class MainActivity : AppCompatActivity() { return } val peer_uri = Api.call_peeruri(callp) - val new_call = Call(callp, ua, peer_uri, "in", "Answer", null) + val new_call = Call(callp, ua, peer_uri, "in", "incoming", + dtmfWatcher(callp)) if (ONE_CALL_ONLY && (calls.size > 0)) { Log.d("Baresip", "Auto-rejecting incoming call $uap/$callp/$peer_uri") ua_hangup(uap, callp, 486, "Busy Here") @@ -400,10 +442,21 @@ class MainActivity : AppCompatActivity() { } else { Log.d("Baresip", "Incoming call $uap/$callp/$peer_uri") calls.add(new_call) - if (ua != uas[aorSpinner.selectedItemPosition]) + if (ua != uas[aorSpinner.selectedItemPosition]) { aorSpinner.setSelection(account_index) - else - addCallViews(ua, new_call, Call.uaCalls(calls, ua, "in").size * 10) + } else { + callTitle.text = "Incoming call from ..." + callUri.setText(new_call.peerURI) + securityButton.visibility = View.INVISIBLE + callButton.visibility = View.INVISIBLE + hangupButton.visibility = View.INVISIBLE + answerButton.visibility = View.VISIBLE + answerButton.isEnabled = true + rejectButton.visibility = View.VISIBLE + rejectButton.isEnabled = true + holdButton.visibility = View.INVISIBLE + dtmf.visibility = View.INVISIBLE + } startRinging() if (Utils.isVisible()) { Log.d("Baresip", "Baresip is visible") @@ -420,58 +473,39 @@ class MainActivity : AppCompatActivity() { Log.e("Baresip", "Established call $callp not found") return } - if (call.dir == "out") { - Log.d("Baresip", "Outbound call $callp established") - call.status = "Hangup" - call.hold = false - call.security = R.drawable.box_red - if (ua == uas[aorSpinner.selectedItemPosition]) { - callButton.tag = "Hangup" - callButton.setImageResource(R.drawable.hangup) - holdButton.tag = "Hold" - holdButton.setImageResource(R.drawable.pause) - holdButton.visibility = View.VISIBLE - if (acc.mediaenc == "") { - securityButton.visibility = View.INVISIBLE - } else { - securityButton.setImageResource(R.drawable.box_red) - securityButton.tag = "red" - securityButton.visibility = View.VISIBLE - } - dtmf.setText("") - dtmf.hint = "DTMF" - dtmf.visibility = View.VISIBLE - dtmf.requestFocus() - dtmf.addTextChangedListener(call.dtmfWatcher) + Log.d("Baresip", "AoR $aor call $callp established") + call.status = "connected" + call.onhold = false + call.security = R.drawable.box_red + if (ua == uas[aorSpinner.selectedItemPosition]) { + if (acc.mediaenc == "") { + securityButton.visibility = View.INVISIBLE + } else { + securityButton.setImageResource(R.drawable.box_red) + securityButton.tag = "red" + securityButton.visibility = View.VISIBLE } - history.add(CallHistory(aor, call.peerURI, "out", true)) - call.hasHistory = true - } else { - Log.d("Baresip", "Inbound call $callp established") - call.status = "Hangup" - call.security = R.drawable.box_red - if (ua == uas[aorSpinner.selectedItemPosition]) { - if (acc.mediaenc != "") { - val inIndex = Call.uaCallIndex(calls, ua, call, "in") - val view_id = (inIndex + 1) * 10 + 3 - val securityButton = findViewById(view_id) as ImageButton - securityButton.setImageResource(R.drawable.box_red) - securityButton.tag = "red" - securityButton.visibility = View.VISIBLE - val rejectButton = findViewById(view_id + 3) as ImageButton - rejectButton.tag = "Hold" - rejectButton.setImageResource(R.drawable.pause) - } - } - history.add(CallHistory(aor, call.peerURI, "in", true)) - call.hasHistory = true + callButton.visibility = View.INVISIBLE + hangupButton.visibility = View.VISIBLE + hangupButton.isEnabled = true + answerButton.visibility = View.INVISIBLE + rejectButton.visibility = View.INVISIBLE + holdButton.setImageResource(R.drawable.pause) + holdButton.visibility = View.VISIBLE + dtmf.setText("") + dtmf.hint = "DTMF" + dtmf.visibility = View.VISIBLE + dtmf.requestFocus() + dtmf.addTextChangedListener(call.dtmfWatcher) } + history.add(CallHistory(aor, call.peerURI, call.dir, true)) + call.hasHistory = true if (rtTimer != null) { rtTimer!!.cancel() rtTimer = null if (rt.isPlaying) rt.stop() } - am.mode = AudioManager.MODE_IN_COMMUNICATION + am.mode = AudioManager . MODE_IN_COMMUNICATION requestAudioFocus(STREAM_VOICE_CALL) am.isSpeakerphoneOn = false // am.setStreamVolume(AudioManager.STREAM_VOICE_CALL, @@ -500,36 +534,20 @@ class MainActivity : AppCompatActivity() { } call.security = security call.zid = ev[3] - if (call.dir == "out") { + if (ua == uas[aorSpinner.selectedItemPosition]) { securityButton.setImageResource(security) setSecurityButtonTag(securityButton, security) securityButton.visibility = View.VISIBLE - } else { - if (ua == uas[aorSpinner.selectedItemPosition]) { - val view_id = (Call.uaCallIndex(calls, ua, call, "in") + 1) * 10 + 3 - val securityButton = layout.findViewById(view_id) as ImageButton - securityButton.setImageResource(security) - setSecurityButtonTag(securityButton, security) - securityButton.visibility = View.VISIBLE - } + dialog.dismiss() } - dialog.dismiss() } verifyDialog.setNegativeButton("No") { dialog, _ -> call.security = R.drawable.box_yellow call.zid = ev[3] - if (call.dir == "out") { + if (ua == uas[aorSpinner.selectedItemPosition]) { securityButton.setImageResource(R.drawable.box_yellow) securityButton.tag = "yellow" securityButton.visibility = View.VISIBLE - } else { - if (ua == uas[aorSpinner.selectedItemPosition]) { - val view_id = (Call.uaCallIndex(calls, ua, call, "in") + 1) * 10 + 3 - val securityButton = layout.findViewById(view_id) as ImageButton - securityButton.setImageResource(R.drawable.box_yellow) - securityButton.tag = "yellow" - securityButton.visibility = View.VISIBLE - } } dialog.dismiss() } @@ -551,20 +569,10 @@ class MainActivity : AppCompatActivity() { tag = "green" call.zid = ev[1] } - if (call.dir == "out") { - if (ua == uas[aorSpinner.selectedItemPosition]) { - securityButton.setImageResource(call.security) - securityButton.tag = tag - securityButton.visibility = View.VISIBLE - } - } else { - if (ua == uas[aorSpinner.selectedItemPosition]) { - val view_id = (Call.uaCallIndex(calls, ua, call, "in") + 1) * 10 + 3 - val securityButton = layout.findViewById(view_id) as ImageButton - securityButton.setImageResource(call.security) - securityButton.tag = tag - securityButton.visibility = View.VISIBLE - } + if (ua == uas[aorSpinner.selectedItemPosition]) { + securityButton.setImageResource(call.security) + securityButton.tag = tag + securityButton.visibility = View.VISIBLE } } "call closed" -> { @@ -574,57 +582,27 @@ class MainActivity : AppCompatActivity() { Log.d("Baresip", "Call $callp that is closed is not found") return } - if (call.dir == "in") { - Log.d("Baresip", "Removing inbound call ${uap}/${callp}/" + - call.peerURI) - val inIndex = Call.uaCallIndex(calls, ua, call, "in") - val view_id = (inIndex + 1) * 10 - val remove_count = Call.uaCalls(calls, ua, "in").size - inIndex - calls.remove(call) - if (ua == uas[aorSpinner.selectedItemPosition]) { - if (callButton.tag == "Call") { - callee.setOnKeyListener(null) - } - val caller_heading = layout.findViewById(view_id) as TextView? - if (caller_heading != null) { - val view_index = layout.indexOfChild(caller_heading) - layout.removeViews(view_index, 3 * remove_count) - val callsIn = Call.uaCalls(calls, ua, "in") - for (i in inIndex until callsIn.size) { - addCallViews(ua, callsIn[i], (i + 1) * 10) - } - } - } - if (!call.hasHistory) { + Log.d("Baresip", "Removing call ${uap}/${callp}/" + call.peerURI) + stopRinging() + if (ua == uas[aorSpinner.selectedItemPosition]) { + callTitle.text = "Outgoing call to ..." + callUri.setText("") + callUri.hint = "Callee" + securityButton.visibility = View.INVISIBLE + callButton.visibility = View.VISIBLE + hangupButton.visibility = View.INVISIBLE + answerButton.visibility = View.INVISIBLE + rejectButton.visibility = View.INVISIBLE + holdButton.visibility = View.INVISIBLE + dtmf.visibility = View.INVISIBLE + imm.hideSoftInputFromWindow(dtmf.windowToken, 0) + dtmf.removeTextChangedListener(call.dtmfWatcher) + } + calls.remove(call) + if (!call.hasHistory) { if (CallHistory.aorHistory(history, aor) > HISTORY_SIZE) CallHistory.aorRemoveHistory(history, aor) - history.add(CallHistory(aor, call.peerURI, "in", false)) - } - stopRinging() - } else { - Log.d("Baresip", "Removing outgoing call $uap/$callp/" + - call.peerURI) - if (ua == uas[aorSpinner.selectedItemPosition]) { - callButton.tag = "Call" - callButton.setImageResource(R.drawable.call_green) - callButton.isEnabled = true - callee.setText("") - callee.hint = "Callee" - holdButton.visibility = View.INVISIBLE - if (currentFocus == dtmf) { - imm.hideSoftInputFromWindow(dtmf.windowToken, 0) - } - securityButton.visibility = View.INVISIBLE - dtmf.removeTextChangedListener(call.dtmfWatcher) - dtmf.visibility = View.INVISIBLE - } - calls.remove(call) - if (!call.hasHistory) { - if (CallHistory.aorHistory(history, aor) > HISTORY_SIZE) - CallHistory.aorRemoveHistory(history, aor) - history.add(CallHistory(aor, call.peerURI, "out", - false)) - } + history.add(CallHistory(aor, call.peerURI, call.dir,false)) } if (calls.size == 0) am.mode = AudioManager.MODE_NORMAL if (am.isSpeakerphoneOn) { @@ -750,22 +728,18 @@ class MainActivity : AppCompatActivity() { override fun onResume() { super.onResume() // Log.d("Baresip", "Resumed") - imm.hideSoftInputFromWindow(callee.windowToken, 0) + imm.hideSoftInputFromWindow(callUri.windowToken, 0) visible = true - if ((answerCall != "") && (layout.childCount > 6)) { + if (answerCall != "") { answerCall = "" - /* if multiple calls, right answer button needs to be searched */ - val answerButton = layout.findViewById(10 + 5) as ImageButton answerButton.performClick() } - if ((rejectCall != "") && (layout.childCount > 6)) { + if (rejectCall != "") { rejectCall = "" - /* if multiple calls, right reject button needs to be searched */ - val rejectButton = layout.findViewById(10 + 6) as ImageButton rejectButton.performClick() } if (makeCall != "") { - callee.setText(makeCall) + callUri.setText(makeCall) makeCall = "" callButton.performClick() } @@ -845,7 +819,7 @@ class MainActivity : AppCompatActivity() { } CONTACTS_CODE -> { - callee.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item, + callUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item, ContactsActivity.contacts.map{Contact -> Contact.name})) } @@ -863,7 +837,7 @@ class MainActivity : AppCompatActivity() { HISTORY_CODE -> { if (resultCode == RESULT_OK) { if (data != null) { - (findViewById(R.id.callee) as EditText).setText(data.getStringExtra("peer_uri")) + (findViewById(R.id.uri) as EditText).setText(data.getStringExtra("peer_uri")) } } if (resultCode == RESULT_CANCELED) { @@ -886,209 +860,40 @@ class MainActivity : AppCompatActivity() { "You have not granted microphone permission.", Toast.LENGTH_SHORT).show() return } - (findViewById(R.id.callee) as EditText).setText(uri) - val call = ua_connect(ua.uap, uri) - if (call != "") { - Log.d("Baresip", "Adding outgoing call ${ua.uap}/$call/$uri") - val dtmfWatcher = object : TextWatcher { - override fun beforeTextChanged(sequence: CharSequence, start: Int, count: Int, after: Int) {} - override fun onTextChanged(sequence: CharSequence, start: Int, before: Int, count: Int) { - val digit = sequence.subSequence(start, start + count).toString() - Log.d("Baresip", "Got DTMF digit '" + digit + "'") - if (digit.isNotEmpty()) call_send_digit(call, digit[0]) - } - override fun afterTextChanged(sequence: Editable) { - call_send_digit(call, 4.toChar()) - } - } - calls.add(Call(call, ua, uri, "out", "Cancel", dtmfWatcher)) - callButton.tag = "Cancel" - callButton.setImageResource(R.drawable.hangup) + (findViewById(R.id.uri) as EditText).setText(uri) + callButton.visibility = View.INVISIBLE + val callp = ua_connect(ua.uap, uri) + if (callp != "") { + Log.d("Baresip", "Adding outgoing call ${ua.uap}/$callp/$uri") + calls.add(Call(callp, ua, uri, "out", "outgoing", dtmfWatcher(callp))) + imm.hideSoftInputFromWindow(callUri.windowToken, 0) + securityButton.visibility = View.INVISIBLE + callButton.visibility = View.INVISIBLE + hangupButton.visibility = View.VISIBLE + hangupButton.isEnabled = true + answerButton.visibility = View.INVISIBLE + rejectButton.visibility = View.INVISIBLE holdButton.visibility = View.INVISIBLE + dtmf.visibility = View.INVISIBLE } else { Log.w("Baresip", "ua_connect ${ua.uap}/$uri failed") + callButton.visibility = View.VISIBLE } } - private fun addCallViews(ua: UserAgent, call: Call, id: Int) { - Log.d("Baresip", "Adding incoming views for UA ${ua.uap} Call ${call.callp}") - val acc = ua.account - val caller_heading = TextView(appContext) - caller_heading.text = "Incoming call from ..." - caller_heading.setTextColor(Color.BLACK) - caller_heading.textSize = 20f - caller_heading.setPadding(10, 20, 0, 0) - caller_heading.id = id - val heading_params = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT) - if (id == 10) - heading_params.addRule(RelativeLayout.BELOW, callButton.id) - else - heading_params.addRule(RelativeLayout.BELOW, id - 10 + 4) - caller_heading.layoutParams = heading_params - // Log.d("Baresip", "Adding incoming call heading at ${caller_heading.id}") - layout.addView(caller_heading) + private fun dtmfWatcher(callp: String): TextWatcher { + return object : TextWatcher { + override fun beforeTextChanged(sequence: CharSequence, start: Int, count: Int, after: Int) {} + override fun onTextChanged(sequence: CharSequence, start: Int, before: Int, count: Int) { + val digit = sequence.subSequence(start, start + count).toString() + Log.d("Baresip", "Got DTMF digit '" + digit + "'") + if (digit.isNotEmpty()) call_send_digit(callp, digit[0]) + } - val caller_row = LinearLayout(applicationContext) - caller_row.id = caller_heading.id + 1 - caller_row.setOrientation(LinearLayout.HORIZONTAL) - val caller_row_params = LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT) - caller_row_params.addRule(RelativeLayout.BELOW, id) - caller_row.layoutParams = caller_row_params - - val caller_uri = TextView(applicationContext) - caller_uri.text = ContactsActivity.contactName(call.peerURI) - caller_uri.setTextColor(Color.BLUE) - caller_uri.textSize = 20f - caller_uri.setPadding(10, 10, 0, 10) - caller_uri.id = caller_row.id + 1 - val caller_uri_params = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, - LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f) - caller_uri.layoutParams = caller_uri_params - // Log.d("Baresip", "Adding caller uri at ${caller_uri.id}") - caller_row.addView(caller_uri) - - val security_button = ImageButton(applicationContext) - security_button.id = caller_uri.id + 1 - val dp24px = ((24 * applicationContext.resources.displayMetrics.density) + 0.5).toInt() - val security_button_params = LinearLayout.LayoutParams(dp24px, dp24px, 0.0f) - security_button_params.gravity = Gravity.CENTER_VERTICAL - security_button.layoutParams = security_button_params - if ((call.security != 0) && (acc.mediaenc != "")) { - security_button.setImageResource(call.security) - setSecurityButtonTag(securityButton, call.security) - security_button.visibility = View.VISIBLE - } else { - security_button.visibility = View.INVISIBLE - } - security_button.setOnClickListener { - when (security_button.tag) { - "red" -> { - Utils.alertView(this, "Alert", "This call is NOT secure!") - } - "yellow" -> { - Utils.alertView(this, "Alert", - "This call is SECURE, but peer is NOT verified!") - } - "green" -> { - val unverifyDialog = AlertDialog.Builder(this) - unverifyDialog.setMessage("This call is SECURE and peer is VERIFIED! " + - "Do you want to unverify the peer?") - unverifyDialog.setPositiveButton("Unverify") { dialog, _ -> - if (Api.cmd_exec("zrtp_unverify " + call.zid) != 0) { - Log.w("Baresip", "Command 'zrtp_unverify ${call.zid}' failed") - } else { - security_button.setImageResource(R.drawable.box_yellow) - security_button.tag = "yellow" - } - dialog.dismiss() - } - unverifyDialog.setNegativeButton("No") { dialog, _ -> - dialog.dismiss() - } - unverifyDialog.create().show() - } + override fun afterTextChanged(sequence: Editable) { + call_send_digit(callp, 4.toChar()) } } - // Log.d("Baresip", "Adding security button at ${security_button.id}") - caller_row.addView(security_button) - - // Log.d("Baresip", "Adding caller row at ${caller_row.id}") - layout.addView(caller_row) - - val answer_row = LinearLayout(applicationContext) - answer_row.id = security_button.id + 1 - answer_row.setOrientation(LinearLayout.HORIZONTAL) - val answer_row_params = LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT) - answer_row_params.addRule(RelativeLayout.BELOW, caller_row.id) - answer_row.layoutParams = answer_row_params - - val answer_button = ImageButton(applicationContext) - answer_button.tag = call.status - if (call.status == "Answer") - answer_button.setImageResource(R.drawable.call_green) - else - answer_button.setImageResource(R.drawable.hangup) - answer_button.background = null - answer_button.id = answer_row.id + 1 - answer_button.setOnClickListener { v -> - when ((v as ImageButton).tag) { - "Answer" -> { - Log.d("Baresip", "UA ${ua.uap} accepting incoming call ${call.callp}") - ua_answer(ua.uap, call.callp) - if (call.dir == "in") { - call.status = "Hangup" - call.hold = false - answer_button.tag = "Hangup" - answer_button.setImageResource(R.drawable.hangup) - val reject_button = layout.findViewById(answer_button.id + 1) as ImageButton - reject_button.tag = "Hold" - reject_button.setImageResource(R.drawable.pause) - } - } - "Hangup" -> { - Log.d("Baresip", "UA ${call.ua.uap} hanging up call ${call.callp}") - answer_button.isEnabled = false - ua_hangup(call.ua.uap, call.callp, 200, "OK") - } - else -> Log.e("Baresip", "Invalid answer button tag: " + v.tag) - } - } - val answer_button_params = LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT) - answer_button_params.setMargins(3, 10, 0, 0) - answer_button.layoutParams = answer_button_params - answer_button.setPadding(0, 0, 0, 0) - // Log.d("Baresip", "Adding answer button at ${answer_button.id}") - answer_row.addView(answer_button) - - val reject_button = ImageButton(appContext) - if (call.status == "Answer") { - reject_button.tag = "Reject" - reject_button.setImageResource(R.drawable.hangup) - } else { - if (call.hold) { - reject_button.tag = "Resume" - reject_button.setImageResource(R.drawable.play) - } else { - reject_button.tag = "Hold" - reject_button.setImageResource(R.drawable.pause) - } - } - reject_button.background = null - reject_button.id = answer_button.id + 1 - reject_button.setOnClickListener { v -> - when ((v as ImageButton).tag) { - "Reject" -> { - Log.d("Baresip", "UA ${call.ua} rejecting incoming call ${call.callp}") - reject_button.isEnabled = false - answer_button.isEnabled = false - ua_hangup(call.ua.uap, call.callp, 486, "Rejected") - } - "Hold" -> { - holdCallAt(call, reject_button) - } - "Resume" -> { - call_unhold(call.callp) - v.tag = "Hold" - reject_button.setImageResource(R.drawable.pause) - call.hold = false - } - } - } - val reject_button_params = RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT) - reject_button_params.topMargin = 10 - reject_button_params.addRule(RelativeLayout.ALIGN_PARENT_END) - reject_button.layoutParams = reject_button_params - reject_button.setPadding(50, 0, 0, 0) - reject_button.background = null - // Log.d("Baresip", "Adding reject button at ${reject_button.id}") - answer_row.addView(reject_button) - - // Log.d("Baresip", "Adding answer row at ${answer_row.id}") - layout.addView(answer_row) } private fun setSecurityButtonTag(button: ImageButton, security: Int) { @@ -1105,14 +910,6 @@ class MainActivity : AppCompatActivity() { } } - private fun holdCallAt(call: Call, button: ImageButton) { - Log.d("Baresip", "Holding call with ${call.peerURI} at ${button.id}") - call_hold(call.callp) - call.hold = true - button.tag = "Resume" - button.setImageResource(R.drawable.play) - } - private fun requestAudioFocus(stream: Int) { if (!audioFocused) { val res = am.requestAudioFocus(null, stream, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) @@ -1148,7 +945,6 @@ class MainActivity : AppCompatActivity() { } } - external fun uag_current(): String external fun uag_current_set(uap: String) external fun ua_connect(uap: String, peer_uri: String): String external fun ua_answer(uap: String, callp: String) diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index f6edfd04..d964f36c 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -31,7 +31,7 @@ android:spinnerMode="dropdown"/> + android:layout_below="@id/callTitle" > + + + + + + + + + + @@ -98,7 +137,7 @@ android:id="@+id/dtmf" android:layout_width="@dimen/button_width" android:layout_height="wrap_content" - android:layout_below="@id/calleeRow" + android:layout_below="@id/uriRow" android:layout_marginStart="140dp" android:inputType="phone" android:textSize="20sp"