package com.tutpro.baresip import android.Manifest import android.app.NotificationManager import android.content.BroadcastReceiver import android.content.Intent import android.support.v4.app.ActivityCompat import android.support.v4.content.ContextCompat import android.support.v7.app.AlertDialog import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.util.Log import android.view.Menu import android.view.MenuItem import android.content.Context import android.content.IntentFilter import android.content.pm.PackageManager import android.graphics.Color import android.media.AudioManager import android.support.v4.content.LocalBroadcastManager import android.support.v7.view.menu.ActionMenuItemView import android.view.inputmethod.InputMethodManager import android.text.Editable import android.text.TextWatcher import android.widget.RelativeLayout.LayoutParams import android.widget.RelativeLayout import android.widget.* import android.view.* 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 securityButton: ImageButton internal lateinit var callButton: ImageButton internal lateinit var holdButton: ImageButton internal lateinit var historyButton: ImageButton internal lateinit var dtmf: EditText internal lateinit var uaAdapter: UaSpinnerAdapter internal lateinit var aorSpinner: Spinner internal lateinit var am: AudioManager internal lateinit var imm: InputMethodManager internal lateinit var nm: NotificationManager internal lateinit var serviceEventReceiver: BroadcastReceiver override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.d("Baresip", "At onCreate") window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) setContentView(R.layout.activity_main) filesPath = applicationContext.filesDir.absolutePath appContext = applicationContext layout = findViewById(R.id.mainActivityLayout) as RelativeLayout callee = findViewById(R.id.callee) as AutoCompleteTextView securityButton = findViewById(R.id.securityButton) as ImageButton callButton = findViewById(R.id.callButton) as ImageButton holdButton = findViewById(R.id.holdButton) as ImageButton historyButton = findViewById(R.id.historyButton) as ImageButton dtmf = findViewById(R.id.dtmf) as EditText am = getSystemService(Context.AUDIO_SERVICE) as AudioManager imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager nm = getSystemService(NOTIFICATION_SERVICE) as NotificationManager serviceEventReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { handleServiceEvent(intent.getStringExtra("event"), intent.getStringExtra("uap" ), intent.getStringExtra("callp")) } } LocalBroadcastManager.getInstance(this).registerReceiver(serviceEventReceiver, IntentFilter("service event")) if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { Log.w("Baresip", "Baresip does not have RECORD_AUDIO permission") ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.RECORD_AUDIO), RECORD_AUDIO_PERMISSION) } aorSpinner = findViewById(R.id.AoRList) as Spinner uaAdapter = UaSpinnerAdapter(applicationContext, uas, images) aorSpinner.adapter = uaAdapter aorSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { val acc = uas[position].account val aor = acc.aor val ua = uas[position] 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) if (History.aorHistory(history, aor) > 0) { historyButton.visibility = View.VISIBLE } else { historyButton.visibility = View.INVISIBLE } securityButton.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.resume) } else { holdButton.setImageResource(R.drawable.hold) } 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() } else { holdButton.visibility = View.INVISIBLE securityButton.visibility = View.INVISIBLE dtmf.visibility = View.INVISIBLE } } val `in` = Call.uaCalls(calls, ua, "in") val view_count = layout.childCount // Log.d("Baresip", "View count is $view_count") if (view_count > 7) layout.removeViews(7, view_count - 7) for (c in `in`) for (call_index in Call.uaCalls(calls, ua,"in").indices) addCallViews(ua, c, (call_index + 1) * 10) } override fun onNothingSelected(parent: AdapterView<*>) { Log.d("Baresip", "Nothing selected") } } aorSpinner.setOnTouchListener { view, event -> if (event.action == MotionEvent.ACTION_DOWN) { if (aorSpinner.selectedItemPosition == -1) { val i = Intent(this, AccountsActivity::class.java) startActivityForResult(i, ACCOUNTS_CODE) true } else { if ((event.x - view.left) < 100) { val i = Intent(this, AccountActivity::class.java) val b = Bundle() b.putString("accp", uas[aorSpinner.selectedItemPosition].account.accp) i.putExtras(b) startActivityForResult(i, ACCOUNT_CODE) true } else { false } } } else { false } } callee.threshold = 2 callee.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item, ContactsActivity.contacts.map{Contact -> Contact.name})) securityButton.setOnClickListener { when (securityButton.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.setTitle("Info") 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) { Log.w("Baresip", "Command 'zrtp_unverify ${outCalls[0].zid}' failed") } else { securityButton.setImageResource(R.drawable.box_yellow) securityButton.tag = "yellow" } } dialog.dismiss() } unverifyDialog.setNegativeButton("No") { dialog, _ -> dialog.dismiss() } unverifyDialog.create().show() } } } 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() 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" } call(ua, uri) } else { val latest = History.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) } } } "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, "") } } } holdButton.tag = "Hold" holdButton.visibility = View.INVISIBLE 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.hold) } } } historyButton.visibility = View.INVISIBLE historyButton.setOnClickListener { val i = Intent(this@MainActivity, HistoryActivity::class.java) val b = Bundle() b.putString("aor", uas[aorSpinner.selectedItemPosition].account.aor) i.putExtras(b) startActivityForResult(i, HISTORY_CODE) } baresipService = Intent(this@MainActivity, BaresipService::class.java) if (!BaresipService.IS_SERVICE_RUNNING) { baresipService.setAction("Start") startService(baresipService) } if (intent.hasExtra("onStartup")) moveTaskToBack(true) } private fun handleServiceEvent(event: String, uap: String, callp: String) { val ua = UserAgent.find(uas, uap)!! val aor = ua.account.aor val acc = ua.account val ev = event.split(",") Log.d("Baresip", "Handling service event ${ev[0]} for $uap/$callp/$aor") for (account_index in uas.indices) { if (uas[account_index].account.aor == aor) { when (ev[0]) { "ua added" -> { uaAdapter.notifyDataSetChanged() if (aorSpinner.selectedItemPosition == -1) aorSpinner.setSelection(0) } "registered", "registering failed", "unregistering" -> { uaAdapter.notifyDataSetChanged() } "call incoming" -> { val peer_uri = Api.call_peeruri(callp) val new_call = Call(callp, ua, peer_uri, "in", "Answer", null) 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") if (History.aorHistory(history, aor) > HISTORY_SIZE) History.aorRemoveHistory(history, aor) history.add(History(aor, peer_uri, "in", false)) } else { Log.d("Baresip", "Incoming call $uap/$callp/$peer_uri") calls.add(new_call) if (ua != uas[aorSpinner.selectedItemPosition]) aorSpinner.setSelection(account_index) else addCallViews(ua, new_call, Call.uaCalls(calls, ua, "in").size * 10) am.mode = AudioManager.MODE_RINGTONE if (Utils.isVisible()) { Log.d("Baresip", "Baresip is visible") val i = Intent(applicationContext, MainActivity::class.java) i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) startActivity(i) } } } "call established" -> { val call = Call.find(calls, callp) if (call == null) { 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.hold) 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) } history.add(History(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.hold) } } history.add(History(aor, call.peerURI, "in", true)) call.hasHistory = true } am.mode = AudioManager.MODE_IN_COMMUNICATION am.isSpeakerphoneOn = false // am.setStreamVolume(AudioManager.STREAM_VOICE_CALL, // (am.getStreamMaxVolume(STREAM_VOICE_CALL) / 2) + 1, // AudioManager.STREAM_VOICE_CALL) if (History.aorHistory(history, aor) > HISTORY_SIZE) History.aorRemoveHistory(history, aor) } "call verify" -> { val call = Call.find(calls, callp) if (call == null) { Log.e("Baresip", "Call $callp to be verified is not found") return } val verifyDialog = AlertDialog.Builder(this@MainActivity) verifyDialog.setTitle("Verify") verifyDialog.setMessage("Do you want to verify SAS <${ev[1]}> <${ev[2]}>?") verifyDialog.setPositiveButton("Yes") { dialog, _ -> val security: Int if (Api.cmd_exec("zrtp_verify ${ev[3]}") != 0) { Log.w("Baresip", "Command 'zrtp_verify ${ev[3]}' failed") security = R.drawable.box_yellow } else { security = R.drawable.box_green } call.security = security call.zid = ev[3] if (call.dir == "out") { 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() } verifyDialog.setNegativeButton("No") { dialog, _ -> call.security = R.drawable.box_yellow call.zid = ev[3] if (call.dir == "out") { 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() } verifyDialog.create().show() } "call verified", "call secure" -> { val call = Call.find(calls, callp) if (call == null) { Log.e("Baresip", "Call $callp that is verified is not found") return } val tag: String if (ev[0] == "call secure") { call.security = R.drawable.box_yellow tag = "yellow" } else { call.security = R.drawable.box_green 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 } } } "call closed" -> { val call = Call.find(calls, callp) if (call == null) { 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 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) } historyButton.visibility = View.VISIBLE } if (!call.hasHistory) { if (History.aorHistory(history, aor) > HISTORY_SIZE) History.aorRemoveHistory(history, aor) history.add(History(aor, call.peerURI, "in", false)) } } else { Log.d("Baresip", "Removing outgoing call $uap/$callp/" + call.peerURI) if (ua == uas[aorSpinner.selectedItemPosition]) { callButton.tag = "Call" callButton.setImageResource(R.drawable.call) 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 historyButton.visibility = View.VISIBLE } calls.remove(call) if (!call.hasHistory) { if (History.aorHistory(history, aor) > HISTORY_SIZE) History.aorRemoveHistory(history, aor) history.add(History(aor, call.peerURI, "out", false)) } } if (calls.size == 0) am.mode = AudioManager.MODE_NORMAL } else -> Log.w("Baresip", "Unknown event '${ev[0]}'") } break } } } override fun onNewIntent(intent: Intent) { setIntent(intent) val action = intent.getStringExtra("action") Log.d("Baresip", "Got onNewIntent action $action") if (action == null) return val uap = intent.getStringExtra("uap") val callp = intent.getStringExtra("callp") val ua = UserAgent.find(uas, intent.getStringExtra("uap"))!! val aor = ua.account.aor val peer_uri = Api.call_peeruri(callp) when (action) { "answer" -> { /* TODO */ } "reject" -> { Log.d("Baresip", "Rejecting incoming call from $peer_uri") ua_hangup(uap, callp, 486, "Busy Here") if (History.aorHistory(history, aor) > HISTORY_SIZE) History.aorRemoveHistory(history, aor) history.add(History(aor, peer_uri, "in", false)) moveTaskToBack(true) } } } override fun onPause() { super.onPause() Log.d("Baresip", "Paused") visible = false } override fun onResume() { super.onResume() Log.d("Baresip", "Resumed") imm.hideSoftInputFromWindow(callee.windowToken, 0) visible = true } override fun onBackPressed() { moveTaskToBack(true) } override fun onDestroy() { Log.d("Baresip", "Destroyed") LocalBroadcastManager.getInstance(this).unregisterReceiver(serviceEventReceiver) if (BaresipService.IS_SERVICE_RUNNING) { baresipService.setAction("Kill") startService(baresipService) } super.onDestroy() } override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.main_menu, menu) menuInflater.inflate(R.menu.speaker_icon, menu) return true } override fun onOptionsItemSelected(item: MenuItem): Boolean { val i: Intent when (item.itemId) { R.id.speakerIcon -> { am.isSpeakerphoneOn = !am.isSpeakerphoneOn val speakerIcon = findViewById(R.id.speakerIcon) as ActionMenuItemView if (am.isSpeakerphoneOn) speakerIcon.setBackgroundColor(Color.rgb(0x04, 0xb4, 0x04)) else speakerIcon.setBackgroundColor(ContextCompat.getColor(applicationContext, R.color.colorPrimary)) return true } R.id.accounts -> { i = Intent(this, AccountsActivity::class.java) startActivityForResult(i, ACCOUNTS_CODE) return true } R.id.contacts -> { i = Intent(this, ContactsActivity::class.java) startActivityForResult(i, CONTACTS_CODE) return true } R.id.config -> { i = Intent(this, EditConfigActivity::class.java) startActivityForResult(i, EDIT_CONFIG_CODE) return true } R.id.about -> { Api.cmd_exec("audio_debug") i = Intent(this, AboutActivity::class.java) startActivityForResult(i, ABOUT_CODE) return true } R.id.quit -> { Log.d("Baresip", "Quiting") if (BaresipService.IS_SERVICE_RUNNING) { baresipService.setAction("Stop"); startService(baresipService) } finish() return true } else -> return super.onOptionsItemSelected(item) } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { when (requestCode) { ACCOUNTS_CODE -> { uaAdapter.notifyDataSetChanged() baresipService.setAction("UpdateNotification") startService(baresipService) } CONTACTS_CODE -> { callee.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item, ContactsActivity.contacts.map{Contact -> Contact.name})) } EDIT_CONFIG_CODE -> { if (resultCode == RESULT_OK) { Utils.alertView(this, "Notice", "You need to restart baresip in order to activate saved config!") reload_config() } if (resultCode == RESULT_CANCELED) { Log.d("Baresip", "Edit config canceled") } } HISTORY_CODE -> { if (resultCode == RESULT_OK) { if (data != null) { (findViewById(R.id.callee) as EditText).setText(data.getStringExtra("peer_uri")) } } if (resultCode == RESULT_CANCELED) { Log.d("Baresip", "History canceled") if (History.aorHistory(history, uas[aorSpinner.selectedItemPosition].account.aor) == 0) { holdButton.visibility = View.INVISIBLE } } } ABOUT_CODE -> { } } } private fun call(ua: UserAgent, uri: String) { (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) holdButton.visibility = View.INVISIBLE } else { Log.w("Baresip", "ua_connect ${ua.uap}/$uri failed") } } 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) 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 = 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() } } } // 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) else answer_button.setImageResource(R.drawable.hangup) 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.hold) } } "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.resume) } else { reject_button.tag = "Hold" reject_button.setImageResource(R.drawable.hold) } } 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.hold) 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) { when (security) { R.drawable.box_red -> { button.tag = "red" } R.drawable.box_yellow -> { button.tag = "yellow" } R.drawable.box_green -> { button.tag = "green" } } } 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.resume) } 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) external fun ua_hold_answer(uap: String, callp: String): Int external fun ua_hangup(uap: String, callp: String, code: Int, reason: String) external fun call_hold(callp: String): Int external fun call_unhold(callp: String): Int external fun call_send_digit(callp: String, digit: Char): Int external fun reload_config(): Int companion object { var uas = ArrayList() internal var images = ArrayList() var history: ArrayList = ArrayList() internal var calls = ArrayList() var filesPath = "" var visible = true const val ACCOUNTS_CODE = 1 const val CONTACTS_CODE = 2 const val EDIT_CONFIG_CODE = 3 const val HISTORY_CODE = 4 const val ABOUT_CODE = 5 const val ACCOUNT_CODE = 6 const val CONTACT_CODE = 7 const val RECORD_AUDIO_PERMISSION = 1 const val HISTORY_SIZE = 100 const val ONE_CALL_ONLY = true const val INCOMING_ID = 102 } }