From 4388c20ec99f84cf77fef3f26f3713488bb54788 Mon Sep 17 00:00:00 2001 From: Juha Heinanen Date: Thu, 21 Jun 2018 19:31:45 +0300 Subject: [PATCH] major change: use service to run baresip --- app/src/main/AndroidManifest.xml | 13 +- app/src/main/kotlin/com/tutpro/baresip/Api.kt | 7 + .../com/tutpro/baresip/BaresipService.kt | 271 ++++++ .../com/tutpro/baresip/ContactsActivity.kt | 8 +- .../com/tutpro/baresip/HistoryActivity.kt | 20 + .../kotlin/com/tutpro/baresip/MainActivity.kt | 885 +++++++----------- .../main/kotlin/com/tutpro/baresip/Utils.kt | 43 +- app/src/main/res/layout/activity_account.xml | 7 - app/src/main/res/values/strings.xml | 6 +- 9 files changed, 675 insertions(+), 585 deletions(-) create mode 100644 app/src/main/kotlin/com/tutpro/baresip/Api.kt create mode 100644 app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index eef152f5..bcf1f704 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ + package="com.tutpro.baresip"> @@ -38,10 +37,10 @@ android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" + android:roundIcon="@mipmap/ic_launcher" android:supportsRtl="true" - android:theme="@style/AppTheme" > - + @@ -108,6 +107,10 @@ android:name="android.support.PARENT_ACTIVITY" android:value="com.tutpro.baresip.MainActivity" /> + + + + diff --git a/app/src/main/kotlin/com/tutpro/baresip/Api.kt b/app/src/main/kotlin/com/tutpro/baresip/Api.kt new file mode 100644 index 00000000..b519a5b9 --- /dev/null +++ b/app/src/main/kotlin/com/tutpro/baresip/Api.kt @@ -0,0 +1,7 @@ +package com.tutpro.baresip + +object Api { + + external fun call_peeruri(callp: String): String + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt new file mode 100644 index 00000000..27a8eeac --- /dev/null +++ b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt @@ -0,0 +1,271 @@ +package com.tutpro.baresip + +import android.app.Notification +import android.app.Notification.VISIBILITY_PUBLIC +import android.app.NotificationManager +import android.app.PendingIntent +import android.app.Service +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.net.NetworkInfo +import android.os.IBinder +import android.support.annotation.Keep +import android.support.v4.app.NotificationCompat +import android.util.Log +import android.view.View +import android.widget.RemoteViews +import java.io.File +import java.io.FileInputStream +import java.io.ObjectInputStream +import android.support.v4.content.LocalBroadcastManager + +class BaresipService: Service() { + + private val LOG_TAG = "Baresip Service" + internal lateinit var intent: Intent + internal lateinit var nm: NotificationManager + internal lateinit var nb: NotificationCompat.Builder + internal lateinit var ni: Intent + internal lateinit var npi: PendingIntent + internal lateinit var nr: BroadcastReceiver + + override fun onCreate() { + + Log.d(LOG_TAG, "At onCreate") + + intent = Intent("com.tutpro.baresip.EVENT") + intent.setPackage("com.tutpro.baresip") + + nm = getSystemService(NOTIFICATION_SERVICE) as NotificationManager + nb = NotificationCompat.Builder(this) + + ni = Intent(this, MainActivity::class.java) + .setAction(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_LAUNCHER) + npi = PendingIntent.getActivity(this, 0, ni, 0) + + nr = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val intentExtras = intent.extras + val info = intentExtras.getParcelable("networkInfo") + Log.d("Baresip", "Got event $info") + if (info.isConnected) { + if (disconnected) { + UserAgent.register(MainActivity.uas) + disconnected = false + } + } else { + disconnected = true + } + } + } + + super.onCreate() + } + + override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { + + when (intent.getAction()) { + + "Start" -> { + Log.i(LOG_TAG, "Received Start Foreground Intent "); + + val assets = arrayOf("accounts", "contacts", "config", "busy.wav", "callwaiting.wav", + "error.wav", "message.wav", "notfound.wav", "ring.wav", "ringback.wav") + val path = applicationContext.filesDir.path + var file = File(path) + if (!file.exists()) { + Log.d(LOG_TAG, "Creating baresip directory") + try { + File(path).mkdirs() + } catch (e: Error) { + Log.e(LOG_TAG, "Failed to create directory: " + e.toString()) + } + + } + for (a in assets) { + file = File("$path/$a") + if (!file.exists()) { + Log.d(LOG_TAG, "Copying asset $a") + Utils.copyAssetToFile(applicationContext, a, "$path/$a") + } else { + Log.d(LOG_TAG, "Asset $a already copied") + } + } + + file = File(path, "history") + if (file.exists()) { + try { + val fis = FileInputStream(file) + val ois = ObjectInputStream(fis) + @SuppressWarnings("unchecked") + MainActivity.history = ois.readObject() as ArrayList + Log.d(LOG_TAG, "Restored History of ${MainActivity.history.size} entries") + ois.close() + fis.close() + } catch (e: Exception) { + Log.w(LOG_TAG, "InputStream exception: - " + e.toString()) + } + } + + ContactsActivity.generateContacts(path + "/contacts") + + Thread(Runnable { baresipStart(path) }).start() + BaresipService.IS_SERVICE_RUNNING = true + registerReceiver(nr, IntentFilter("android.net.conn.CONNECTIVITY_CHANGE")) + showNotification() + } + + "UpdateNotification" -> { + Log.i(LOG_TAG, "UpdateNotification") + updateNotification() + } + + "Stop" -> { + Log.i(LOG_TAG, "Received Stop Foreground Intent") + baresipStop() + BaresipService.IS_SERVICE_RUNNING = false + unregisterReceiver(nr) + stopForeground(true) + stopSelf() + } + } + + return START_STICKY; + } + + private fun showNotification() { + nb.setVisibility(VISIBILITY_PUBLIC) + .setSmallIcon(R.drawable.ic_stat) + .setContentIntent(npi) + .setPriority(Notification.PRIORITY_MAX) + .setOngoing(true) + .setContent(RemoteViews(packageName, R.layout.notification)) + startForeground(STATUS_NOTIFICATION_ID, nb.build()); + } + + @Keep + fun addUA(uap: String) { + Log.d(LOG_TAG, "addUA at BaresipService") + val ua = UserAgent(uap) + MainActivity.uas.add(ua) + if (UserAgent.ua_isregistered(uap)) { + Log.d(LOG_TAG, "Ua ${ua.account.aor} is registered") + MainActivity.images.add(R.drawable.dot_green) + } else { + Log.d(LOG_TAG, "Ua ${ua.account.aor} is NOT registered") + MainActivity.images.add(R.drawable.dot_yellow) + ua.register() + } + val intent = Intent("service event") + intent.putExtra("event", "ua added") + intent.putExtra("uap", uap) + intent.putExtra("callp", "") + LocalBroadcastManager.getInstance(this).sendBroadcast(intent) + updateNotification() + } + + @Keep + fun updateStatus(event: String, uap: String, callp: String) { + Log.d(LOG_TAG, "updateStatus got event $event") + if (!IS_SERVICE_RUNNING) return + val ua = UserAgent.find(MainActivity.uas, uap) + if (ua == null) { + Log.e(LOG_TAG, "updateStatus did not find ua $uap") + return + } + val aor = ua.account.aor + for (account_index in MainActivity.uas.indices) { + if (MainActivity.uas[account_index].account.aor == aor) { + when (event) { + "registering", "unregistering" -> { + return + } + "registered" -> { + MainActivity.images[account_index] = R.drawable.dot_green + updateNotification() + if (!MainActivity.visible) return + } + "registering failed" -> { + MainActivity.images[account_index] = R.drawable.dot_red + updateNotification() + if (!MainActivity.visible) return + } + "call ringing" -> { + return + } + "call incoming" -> { + if (!Utils.isVisible()) { + Log.d(LOG_TAG, "Baresip is NOT visible") + val peer_uri = Api.call_peeruri(callp) + val huBuilder = NotificationCompat.Builder(this) + .setSmallIcon(R.drawable.ic_stat) + .setContentText("Incoming call from $peer_uri") + .setDefaults(Notification.DEFAULT_ALL) + .setPriority(Notification.PRIORITY_HIGH) + .setAutoCancel(true) + .setContentIntent(npi) + nm.notify(INCOMING_NOTIFICATION_ID, huBuilder.build()) + } + } + "call established", "call closed" -> { + nb.mActions.clear() + nb.mContentText = "" + nm.notify(STATUS_NOTIFICATION_ID, nb.build()) + } + } + } + } + val intent = Intent("service event") + intent.putExtra("event", event) + intent.putExtra("uap", uap) + intent.putExtra("callp", callp) + LocalBroadcastManager.getInstance(this).sendBroadcast(intent) + } + + override fun onBind(intent: Intent): IBinder? { + return null + } + + override fun onDestroy() { + Log.i(LOG_TAG, "In onDestroy") + super.onDestroy(); + } + + fun updateNotification() { + val contentView = RemoteViews(getPackageName(), R.layout.notification) + for (i: Int in 0 .. 5) { + val resID = resources.getIdentifier("status$i", "id", packageName) + if (i < MainActivity.images.size) { + contentView.setImageViewResource(resID, MainActivity.images[i]) + contentView.setViewVisibility(resID, View.VISIBLE) + } else { + contentView.setViewVisibility(resID, View.INVISIBLE) + } + } + if (MainActivity.images.size > 4) + contentView.setViewVisibility(R.id.etc, View.VISIBLE) + else + contentView.setViewVisibility(R.id.etc, View.INVISIBLE) + nb.setContent(contentView) + nm.notify(STATUS_NOTIFICATION_ID, nb.build()) + } + + external fun baresipStart(path: String) + external fun baresipStop() + + companion object { + + var IS_SERVICE_RUNNING = false + val STATUS_NOTIFICATION_ID = 101 + val INCOMING_NOTIFICATION_ID = 102 + var disconnected = false + + } + + init { + System.loadLibrary("baresip") + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/tutpro/baresip/ContactsActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/ContactsActivity.kt index 56117c60..7b7cfe7b 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/ContactsActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ContactsActivity.kt @@ -85,14 +85,14 @@ class ContactsActivity : AppCompatActivity() { fun generateContacts(path: String) { val content = Utils.getFileContents(File(path)) - MainActivity.contacts_remove() + contacts_remove() contacts.clear() content.lines().forEach { val parts = it.split("\"") if (parts.size == 3) { val name = parts[1] val uri = parts[2].trim() - MainActivity.contact_add("\"$name\" $uri") + contact_add("\"$name\" $uri") contacts.add(Contact(name, uri)) } } @@ -121,6 +121,10 @@ class ContactsActivity : AppCompatActivity() { if (c.name.equals(name, ignoreCase = true)) return true return false } + + external fun contacts_remove() + external fun contact_add(contact: String) + } } diff --git a/app/src/main/kotlin/com/tutpro/baresip/HistoryActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/HistoryActivity.kt index 81267e8b..750d3b66 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/HistoryActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/HistoryActivity.kt @@ -10,6 +10,10 @@ import android.util.Log import android.view.MenuItem import android.widget.AdapterView import android.widget.ListView +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import java.io.ObjectOutputStream import java.text.SimpleDateFormat import java.util.ArrayList @@ -147,4 +151,20 @@ class HistoryActivity : AppCompatActivity() { now.get(Calendar.DAY_OF_MONTH) == time.get(Calendar.DAY_OF_MONTH) } + companion object { + + fun saveHistory() { + val file = File(MainActivity.filesPath, "history") + try { + val fos = FileOutputStream(file) + val oos = ObjectOutputStream(fos) + oos.writeObject(MainActivity.history) + oos.close() + fos.close() + } catch (e: IOException) { + Log.w("Baresip", "OutputStream exception: " + e.toString()) + e.printStackTrace() + } + } + } } diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt index a0c4395c..886058db 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt @@ -1,41 +1,37 @@ package com.tutpro.baresip import android.Manifest -import android.app.Notification.VISIBILITY_PUBLIC import android.app.NotificationManager -import android.app.PendingIntent -import android.content.* -import android.content.pm.PackageManager -import android.content.res.Configuration -import android.graphics.Color +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.support.annotation.Keep -import android.widget.* -import android.widget.RelativeLayout.LayoutParams -import android.widget.AutoCompleteTextView -import android.view.* import android.util.Log -import android.widget.RelativeLayout +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.net.NetworkInfo -import android.support.v7.app.NotificationCompat +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.view.inputmethod.InputMethodManager -import android.widget.EditText - -import java.util.* -import java.io.* +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 @@ -47,19 +43,22 @@ class MainActivity : AppCompatActivity() { internal lateinit var am: AudioManager internal lateinit var imm: InputMethodManager internal lateinit var nm: NotificationManager - internal lateinit var nb: NotificationCompat.Builder - internal lateinit var receiver: BroadcastReceiver + 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 @@ -68,32 +67,27 @@ class MainActivity : AppCompatActivity() { holdButton = findViewById(R.id.holdButton) as ImageButton historyButton = findViewById(R.id.historyButton) as ImageButton dtmf = findViewById(R.id.dtmf) as EditText - filesPath = applicationContext.filesDir.absolutePath am = getSystemService(Context.AUDIO_SERVICE) as AudioManager imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager nm = getSystemService(NOTIFICATION_SERVICE) as NotificationManager - nb = NotificationCompat.Builder(this) - nb.setVisibility(VISIBILITY_PUBLIC).setOngoing(true).setSmallIcon(R.drawable.ic_stat) - .setContentIntent( - PendingIntent.getActivity(this, NOTIFICATION_ID, - Intent(this, MainActivity::class.java) - .setAction(Intent.ACTION_MAIN) - .addCategory(Intent.CATEGORY_LAUNCHER), - PendingIntent.FLAG_UPDATE_CURRENT)) - .setContent(RemoteViews(getPackageName(), R.layout.notification)) - - val netFilter = IntentFilter("android.net.conn.CONNECTIVITY_CHANGE") - receiver = object : BroadcastReceiver() { + serviceEventReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { - val intentExtras = intent.extras - val info = intentExtras.getParcelable("networkInfo") - Log.d("Baresip", "Got event $info") - if (running) UserAgent.register(uas) + handleServiceEvent(intent.getStringExtra("event"), + intent.getStringExtra("uap" ), + intent.getStringExtra("callp")) } } - registerReceiver(receiver, netFilter) + LocalBroadcastManager.getInstance(this).registerReceiver(serviceEventReceiver, + IntentFilter("service event")) + + if (ContextCompat.checkSelfPermission(this, + Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { + Log.d("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) @@ -180,53 +174,6 @@ class MainActivity : AppCompatActivity() { } } - val assets = arrayOf("accounts", "contacts", "config", "busy.wav", "callwaiting.wav", - "error.wav", "message.wav", "notfound.wav", "ring.wav", "ringback.wav") - val path = applicationContext.filesDir.path - Log.d("Baresip", "path is: $path") - var file = File(path) - if (!file.exists()) { - Log.d("Baresip", "Creating baresip directory") - try { - File(path).mkdirs() - } catch (e: Error) { - Log.e("Baresip", "Failed to create directory: " + e.toString()) - } - - } - for (a in assets) { - file = File("$path/$a") - if (!file.exists()) { - Log.d("Baresip", "Copying asset $a") - copyAssetToFile(applicationContext, a, "$path/$a") - } else { - Log.d("Baresip", "Asset $a already copied") - } - } - - file = File(path, "history") - try { - val fis = FileInputStream(file) - val ois = ObjectInputStream(fis) - @SuppressWarnings("unchecked") - history = ois.readObject() as ArrayList - Log.d("Baresip", "Restored History of ${history.size} entries") - ois.close() - fis.close() - } catch (e: Exception) { - Log.w("Baresip", "InputStream exception: - " + e.toString()) - } - - if (ContextCompat.checkSelfPermission(this, - Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { - Log.d("Baresip", "Baresip does not have RECORD_AUDIO permission") - ActivityCompat.requestPermissions(this, - arrayOf(Manifest.permission.RECORD_AUDIO), RECORD_AUDIO_PERMISSION) - } - - ContactsActivity.generateContacts(applicationContext.filesDir.absolutePath + - "/contacts") - callee.threshold = 2 callee.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item, ContactsActivity.contacts.map{Contact -> Contact.name})) @@ -335,18 +282,266 @@ class MainActivity : AppCompatActivity() { startActivityForResult(i, HISTORY_CODE) } - if (!running) { - Log.i("Baresip", "Starting Baresip with path $path") - if (!Utils.isConnected(this)) { - Toast.makeText(this, "No network connection!", - Toast.LENGTH_LONG).show() - unregisterReceiver(receiver) - finish() - // System.exit(0) - } else { - nm.notify(NOTIFICATION_ID, nb.build()) - Thread(Runnable { baresipStart(path) }).start() - running = true + baresipService = Intent(this@MainActivity, BaresipService::class.java) + if (!BaresipService.IS_SERVICE_RUNNING) { + baresipService.setAction("Start") + startService(baresipService) + } + } + + 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" -> { + uaAdapter.notifyDataSetChanged() + } + "registering failed" -> { + uaAdapter.notifyDataSetChanged() + } + "call ringing" -> { + } + "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") + history.add(History(aor, peer_uri, "in", false)) + new_call.hasHistory = true + } 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 (Utils.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) + 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) { + this@MainActivity.addCallViews(ua, callsIn[i], (i + 1) * 10) + } + historyButton.visibility = View.VISIBLE + } + // nm.cancel(INCOMING_ID) + 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.d("Baresip", "Unknown event '${ev[0]}'") + } + break } } } @@ -354,48 +549,30 @@ class MainActivity : AppCompatActivity() { 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) - nm.cancel(INCOMING_ID) - } - - override fun onConfigurationChanged(newConfig: Configuration) { - super.onConfigurationChanged(newConfig) - if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { - Log.d("Baresip", "Screen orientation change to landscape") - } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { - Log.d("Baresip", "Screen orientation change to portrait") - } + visible = true + // nm.cancel(INCOMING_ID) } override fun onBackPressed() { moveTaskToBack(true) } - override fun onRequestPermissionsResult(requestCode: Int, - permissions: Array, grantResults: IntArray) { - when (requestCode) { - RECORD_AUDIO_PERMISSION -> { - if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - Log.d("Baresip", "RECORD_AUDIO permission granted") - } else { - Log.d("Baresip", "RECORD_AUDIO permission NOT granted") - } - return - } - else -> Log.e("Baresip", "Unknown permissions request code: $requestCode") - } + override fun onDestroy() { + Log.d("Baresip", "Destroyed") + LocalBroadcastManager.getInstance(this).unregisterReceiver(serviceEventReceiver) + super.onDestroy() } override fun onCreateOptionsMenu(menu: Menu): Boolean { - super.onCreateOptionsMenu(menu) - val inflater = menuInflater - inflater.inflate(R.menu.main_menu, menu) - inflater.inflate(R.menu.speaker_icon, menu) + menuInflater.inflate(R.menu.main_menu, menu) + menuInflater.inflate(R.menu.speaker_icon, menu) return true } @@ -434,41 +611,30 @@ class MainActivity : AppCompatActivity() { return true } R.id.quit -> { - if (running) { - Log.d("Baresip", "Stopping") - val path = applicationContext.filesDir.path - Log.d("Baresip", "Saving history to $path") - val file = File(path, "history") - try { - val fos = FileOutputStream(file) - val oos = ObjectOutputStream(fos) - oos.writeObject(history) - oos.close() - fos.close() - } catch (e: IOException) { - Log.w("Baresip", "OutputStream exception: " + e.toString()) - e.printStackTrace() - } - baresipStop() - nm.cancelAll() - running = false + Log.d("Baresip", "Quiting") + if (BaresipService.IS_SERVICE_RUNNING) { + HistoryActivity.saveHistory() + baresipService.setAction("Stop"); + startService(baresipService) } - unregisterReceiver(receiver) + // nm.cancelAll() finish() - System.exit(0) + // System.exit(0) return true } else -> return super.onOptionsItemSelected(item) } } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { when (requestCode) { ACCOUNTS_CODE -> { uaAdapter.notifyDataSetChanged() - updateNotification() + baresipService.setAction("UpdateNotification") + startService(baresipService) } CONTACTS_CODE -> { @@ -508,10 +674,10 @@ class MainActivity : AppCompatActivity() { private fun call(ua: UserAgent, uri: String) { (findViewById(R.id.callee) as EditText).setText(uri) - Log.i("Baresip", "Calling $ua.uap/$uri") + Log.i("Baresip", "Calling ${ua.uap}/$uri") val call = ua_connect(ua.uap, uri) if (call != "") { - Log.i("Baresip", "Adding outgoing call $ua.uap / $call / $uri") + Log.i("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) { @@ -539,8 +705,7 @@ class MainActivity : AppCompatActivity() { 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) + val heading_params = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT) if (id == 10) heading_params.addRule(RelativeLayout.BELOW, callButton.id) else @@ -549,7 +714,7 @@ class MainActivity : AppCompatActivity() { Log.d("Baresip", "Adding incoming call heading at ${caller_heading.id}") layout.addView(caller_heading) - val caller_row = LinearLayout(appContext) + 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, @@ -557,7 +722,7 @@ class MainActivity : AppCompatActivity() { caller_row_params.addRule(RelativeLayout.BELOW, id) caller_row.layoutParams = caller_row_params - val caller_uri = TextView(appContext) + val caller_uri = TextView(applicationContext) caller_uri.text = call.peerURI caller_uri.setTextColor(Color.BLUE) caller_uri.textSize = 20f @@ -569,13 +734,13 @@ class MainActivity : AppCompatActivity() { Log.d("Baresip", "Adding caller uri at ${caller_uri.id}") caller_row.addView(caller_uri) - val security_button = ImageButton(appContext) + val security_button = ImageButton(applicationContext) security_button.id = caller_uri.id + 1 - val dp24px = ((24 * appContext.resources.displayMetrics.density) + 0.5).toInt() + 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 == "zrtp") || (acc.mediaenc == "dtls_srtpf"))) { + if ((call.security != 0) && (acc.mediaenc != "")) { security_button.setImageResource(call.security) setSecurityButtonTag(securityButton, call.security) security_button.visibility = View.VISIBLE @@ -617,7 +782,7 @@ class MainActivity : AppCompatActivity() { Log.d("Baresip", "Adding caller row at ${caller_row.id}") layout.addView(caller_row) - val answer_row = LinearLayout(appContext) + 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, @@ -625,7 +790,7 @@ class MainActivity : AppCompatActivity() { answer_row_params.addRule(RelativeLayout.BELOW, caller_row.id) answer_row.layoutParams = answer_row_params - val answer_button = ImageButton(appContext) + val answer_button = ImageButton(applicationContext) answer_button.tag = call.status if (call.status == "Answer") answer_button.setImageResource(R.drawable.call) @@ -638,15 +803,13 @@ class MainActivity : AppCompatActivity() { Log.i("Baresip", "UA ${ua.uap} accepting incoming call ${call.callp}") ua_answer(ua.uap, call.callp) if (call.dir == "in") { - this@MainActivity.runOnUiThread { - 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) - } + 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" -> { @@ -695,11 +858,13 @@ class MainActivity : AppCompatActivity() { } } } - val reject_button_params = LayoutParams(LayoutParams.WRAP_CONTENT, + val reject_button_params = RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT) - reject_button_params.setMargins(40, 10, 0, 0) + reject_button_params.topMargin = 10 + reject_button_params.addRule(RelativeLayout.ALIGN_PARENT_END) reject_button.layoutParams = reject_button_params - reject_button.setPadding(0, 0, 0, 0) + 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) @@ -707,352 +872,6 @@ class MainActivity : AppCompatActivity() { layout.addView(answer_row) } - fun updateNotification() { - val contentView = RemoteViews(getPackageName(), R.layout.notification) - for (i: Int in 0 .. 5) { - val resID = resources.getIdentifier("status$i", "id", packageName) - if (i < images.size) { - contentView.setImageViewResource(resID, images[i]) - contentView.setViewVisibility(resID, View.VISIBLE) - } else { - contentView.setViewVisibility(resID, View.INVISIBLE) - } - } - if (images.size > 4) - contentView.setViewVisibility(R.id.etc, View.VISIBLE) - else - contentView.setViewVisibility(R.id.etc, View.INVISIBLE) - nb.setContent(contentView) - nm.notify(NOTIFICATION_ID, nb.build()) - } - - @Keep - fun addUA(uap: String) { - val ua = UserAgent(uap) - uas.add(ua) - if (UserAgent.ua_isregistered(uap)) { - Log.d("Baresip", "Ua $ua is registered") - images.add(R.drawable.dot_green) - } else { - Log.d("Baresip", "Ua $ua is NOT registered") - images.add(R.drawable.dot_yellow) - ua.register() - } - this@MainActivity.runOnUiThread { - uaAdapter.notifyDataSetChanged() - if (aorSpinner.selectedItemPosition == -1) aorSpinner.setSelection(0) - updateNotification() - } - } - - @Keep - private fun updateStatus(event: String, uap: String, callp: String) { - - val ua = UserAgent.find(uas, uap) - if (ua == null) { - Log.e("Baresip", "Update status did not find ua $uap") - return - } - val aor = ua.account.aor - val acc = ua.account - val ev = event.split(",") - - Log.d("Baresip", "Handling event ${ev[0]} for $uap/$callp/$aor") - - for (account_index in uas.indices) { - if (uas[account_index].account.aor == aor) { - Log.d("Baresip", "Found AoR at index $account_index") - when (ev[0]) { - "registering", "unregistering" -> { - } - "registered" -> { - Log.d("Baresip", "Setting status to green") - this@MainActivity.runOnUiThread { - images[account_index] = R.drawable.dot_green - uaAdapter.notifyDataSetChanged() - updateNotification() - } - } - "registering failed" -> { - Log.d("Baresip", "Setting status to red") - this@MainActivity.runOnUiThread { - images[account_index] = R.drawable.dot_red - uaAdapter.notifyDataSetChanged() - updateNotification() - } - } - "call ringing" -> { - } - "call incoming" -> { - val peer_uri = 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") - history.add(History(aor, peer_uri, "in", false)) - new_call.hasHistory = true - } else { - Log.d("Baresip", "Incoming call $uap/$callp/$peer_uri") - calls.add(new_call) - this@MainActivity.runOnUiThread { - 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.foregrounded()) { - val i = Intent(this, MainActivity::class.java) - i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) - startActivity(i) - } else { - showNotification(peer_uri) - } - } - } - } - "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 - this@MainActivity.runOnUiThread { - 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 - this@MainActivity.runOnUiThread { - 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 - } - this@MainActivity.runOnUiThread { - val verifyDialog = AlertDialog.Builder(this) - verifyDialog.setTitle("Verify") - verifyDialog.setMessage("Do you want to verify SAS <${ev[1]}> <${ev[2]}>?") - verifyDialog.setPositiveButton("Yes") { dialog, _ -> - val security: Int - if (Utils.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") { - this@MainActivity.runOnUiThread { - if (ua == uas[aorSpinner.selectedItemPosition]) { - securityButton.setImageResource(call.security) - securityButton.tag = tag - securityButton.visibility = View.VISIBLE - } - } - } else { - this@MainActivity.runOnUiThread { - 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) - this@MainActivity.runOnUiThread { - if (ua == uas[aorSpinner.selectedItemPosition]) { - if (callButton.tag == "Call") { - callee.setOnKeyListener(null) - } - val caller_heading = layout.findViewById(view_id) - 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) { - this@MainActivity.addCallViews(ua, callsIn[i], (i + 1) * 10) - } - historyButton.visibility = View.VISIBLE - } - nm.cancel(INCOMING_ID) - } - 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) - this@MainActivity.runOnUiThread { - 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 (this.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.d("Baresip", "Unknown event '${ev[0]}'") - } - break - } - } - } - - private fun copyAssetToFile(context: Context, asset: String, path: String) { - try { - val `is` = context.assets.open(asset) - val os = FileOutputStream(path) - val buffer = ByteArray(512) - var byteRead: Int = `is`.read(buffer) - while (byteRead != -1) { - os.write(buffer, 0, byteRead) - byteRead = `is`.read(buffer) - } - } catch (e: IOException) { - Log.e("Baresip", "Failed to read asset " + asset + ": " + - e.toString()) - } - - } - private fun setSecurityButtonTag(button: ImageButton, security: Int) { when (security) { R.drawable.box_red -> { button.tag = "red" } @@ -1069,34 +888,12 @@ class MainActivity : AppCompatActivity() { button.setImageResource(R.drawable.resume) } - private fun showNotification(peerUri: String) { - Log.d("Baresip", "Showing notification") - val nb = NotificationCompat.Builder(this) - .setVisibility(VISIBILITY_PUBLIC) - .setSmallIcon(R.drawable.ic_stat) - .setContentTitle("Incoming call from") - .setContentText("$peerUri") - .setDefaults(NotificationCompat.DEFAULT_ALL) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setAutoCancel(true) - .setContentIntent( - PendingIntent.getActivity(this, INCOMING_ID, - Intent(this, MainActivity::class.java) - .setAction(Intent.ACTION_MAIN) - .addCategory(Intent.CATEGORY_LAUNCHER), - PendingIntent.FLAG_UPDATE_CURRENT)) - nm.notify(INCOMING_ID, nb.build()) - } - - external fun baresipStart(path: String) - external fun baresipStop() 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_peeruri(callp: String): 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 @@ -1104,12 +901,12 @@ class MainActivity : AppCompatActivity() { companion object { - internal var running: Boolean = false var uas = ArrayList() internal var images = ArrayList() - internal var calls = ArrayList() var history: ArrayList = ArrayList() + internal var calls = ArrayList() var filesPath = "" + var visible = true const val ACCOUNTS_CODE = 1 const val CONTACTS_CODE = 2 @@ -1121,17 +918,9 @@ class MainActivity : AppCompatActivity() { const val RECORD_AUDIO_PERMISSION = 1 const val HISTORY_SIZE = 100 - const val NOTIFICATION_ID = 10 - const val INCOMING_ID = 11 - const val ONE_CALL_ONLY = true - external fun contacts_remove() - external fun contact_add(contact: String) + const val INCOMING_ID = 102 - init { - System.loadLibrary("baresip") - } } - } diff --git a/app/src/main/kotlin/com/tutpro/baresip/Utils.kt b/app/src/main/kotlin/com/tutpro/baresip/Utils.kt index bb94492d..723c9754 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Utils.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Utils.kt @@ -4,13 +4,11 @@ import android.app.ActivityManager import android.content.Context import android.support.v7.app.AlertDialog import android.util.Log -import android.net.ConnectivityManager - -import java.io.* -import android.os.Build import android.os.PowerManager import android.app.KeyguardManager +import java.io.* + object Utils { fun getFileContents(file: File): String { @@ -56,6 +54,25 @@ object Utils { } + fun copyAssetToFile(context: Context, asset: String, path: String) { + try { + val `is` = context.assets.open(asset) + val os = FileOutputStream(path) + val buffer = ByteArray(512) + var byteRead: Int = `is`.read(buffer) + while (byteRead != -1) { + os.write(buffer, 0, byteRead) + byteRead = `is`.read(buffer) + } + os.close() + `is`.close() + } catch (e: IOException) { + Log.e("Baresip", "Failed to read asset " + asset + ": " + + e.toString()) + } + + } + fun alertView(context: Context, title: String, message: String) { // val alertDialog = AlertDialog.Builder(context, android.R.style.Theme_Material_Dialog_Alert).create() val alertDialog = AlertDialog.Builder(context).create() @@ -142,22 +159,10 @@ object Utils { return res } - fun isConnected(context: Context): Boolean { - val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - val activeNetwork = cm.activeNetworkInfo - if (activeNetwork != null && activeNetwork.isConnected) { - val networkType = activeNetwork.type - return networkType == ConnectivityManager.TYPE_WIFI || networkType == ConnectivityManager.TYPE_MOBILE - } else { - return false - } - } - - fun foregrounded(): Boolean { + fun isVisible(): Boolean { val appProcessInfo = ActivityManager.RunningAppProcessInfo() - ActivityManager.getMyMemoryState(appProcessInfo); - return ((appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) or - (appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE)) + ActivityManager.getMyMemoryState(appProcessInfo) + return appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND } fun isDeviceLocked(context: Context): Boolean { diff --git a/app/src/main/res/layout/activity_account.xml b/app/src/main/res/layout/activity_account.xml index 768a03be..f2c5c39e 100644 --- a/app/src/main/res/layout/activity_account.xml +++ b/app/src/main/res/layout/activity_account.xml @@ -21,7 +21,6 @@ android:layout_height="wrap_content" android:textSize="18sp" android:textColor="@android:color/black" - android:onClick="onClick" android:focusable="true" android:focusableInTouchMode="true" android:text="Display Name" > @@ -42,7 +41,6 @@ android:layout_height="wrap_content" android:textSize="18sp" android:textColor="@android:color/black" - android:onClick="onClick" android:text="Authentication Username" > @@ -60,7 +58,6 @@ android:layout_height="wrap_content" android:textSize="18sp" android:textColor="@android:color/black" - android:onClick="onClick" android:text="Authentication Password" > @@ -79,7 +76,6 @@ android:layout_height="wrap_content" android:textSize="18sp" android:textColor="@android:color/black" - android:onClick="onClick" android:text="Outbound Proxies" > @@ -107,7 +103,6 @@ android:layout_height="wrap_content" android:textSize="18sp" android:textColor="@android:color/black" - android:onClick="onClick" android:text="Registration Interval (sec)" > @@ -125,7 +120,6 @@ android:layout_height="wrap_content" android:textSize="18sp" android:textColor="@android:color/black" - android:onClick="onClick" android:text="Audio Codecs" > @@ -143,7 +137,6 @@ android:paddingTop="10dp" android:textSize="18sp" android:textColor="@android:color/black" - android:onClick="onClick" android:text="Media Encryption" > diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 27f2447c..5908c203 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -27,10 +27,8 @@ Selects media transport encryption protocol.\n ZRTP (recommended) means that ZRTP end-to-end media encryption negotiation is tried after the call has been established.\n - DTLS-SRTP means that UDP/TLS/RTP/SAVP is offered in outgoing call and that RTP/SAVP, - RTP/SAVPF, or UDP/TLS/RTP/SAVP is used if offered in incoming call.\n - SRTP-MANF means that RTP/SAVPS is offered in outgoing call and that RTP/SAVP or RTP/SAVPF - is used if offered in incoming call.\n + DTLS-SRTPF means that UDP/TLS/RTP/SAVPF is offered in outgoing call and that RTP/SAVP, + RTP/SAVPF, UDP/TLS/RTP/SAVP, or UDP/TLS/RTP/SAVPF is used if offered in incoming call.\n SRTP-MAND means that RTP/SAVP is offered in outgoing call and that RTP/SAVP or RTP/SAVPF is used if offered in incoming call.\n SRTP means that RTP/AVP is offered in outgoing call and that RTP/SAVP or RTP/SAVPF is used