diff --git a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt index 9e1e61ae..9099fd93 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt @@ -26,7 +26,6 @@ import androidx.annotation.Keep import androidx.annotation.StringRes import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat -import androidx.localbroadcastmanager.content.LocalBroadcastManager import java.io.File import java.net.InetAddress import java.nio.charset.StandardCharsets @@ -75,7 +74,7 @@ class BaresipService: Service() { override fun onCreate() { super.onCreate() - Log.d(TAG, "At onCreate") + Log.d(TAG, "BaresipService onCreate") intent = Intent("com.tutpro.baresip.EVENT") intent.setPackage("com.tutpro.baresip") @@ -555,13 +554,15 @@ class BaresipService: Service() { else status[account_index] = R.drawable.dot_green updateStatusNotification() - registrationUpdate.postValue(System.currentTimeMillis()) + if (isMainVisible) + registrationUpdate.postValue(System.currentTimeMillis()) return } "registering failed" -> { status[account_index] = R.drawable.dot_red updateStatusNotification() - registrationUpdate.postValue(System.currentTimeMillis()) + if (isMainVisible) + registrationUpdate.postValue(System.currentTimeMillis()) if (Utils.isVisible()) { val reason = if (ev.size > 1) { if (ev[1] == "Invalid argument") // Likely due to DNS lookup failure @@ -577,7 +578,8 @@ class BaresipService: Service() { "unregistering" -> { status[account_index] = R.drawable.dot_white updateStatusNotification() - registrationUpdate.postValue(System.currentTimeMillis()) + if (isMainVisible) + registrationUpdate.postValue(System.currentTimeMillis()) return } "call outgoing" -> { @@ -708,7 +710,7 @@ class BaresipService: Service() { call.onhold = false if (ua.account.callHistory) call.startTime = GregorianCalendar() - if (!Utils.isVisible()) + if (!isMainVisible) return } "call update" -> { @@ -716,6 +718,8 @@ class BaresipService: Service() { "0", "1" -> call!!.held = true "2", "3" -> call!!.held = false } + if (!isMainVisible || call!!.status != "connected") + return } "call verified", "call secure" -> { if (ev[0] == "call secure") { @@ -724,7 +728,7 @@ class BaresipService: Service() { call!!.security = R.drawable.box_green call.zid = ev[1] } - if (!Utils.isVisible()) + if (!isMainVisible) return } "call transfer" -> { @@ -782,7 +786,10 @@ class BaresipService: Service() { Log.d(TAG, "AoR $aor call $callp transfer failed: ${ev[1]}") stopMediaPlayer() call!!.referTo = "" - if (!Utils.isVisible()) return + if (Utils.isVisible()) + toast("${getString(R.string.transfer_failed)}: ${ev[1].trim()}") + if (!isMainVisible) + return } "call closed" -> { nm.cancel(CALL_NOTIFICATION_ID) @@ -813,63 +820,68 @@ class BaresipService: Service() { NewCallHistory.save() ua.account.missedCalls = ua.account.missedCalls || missed } - if (!Utils.isVisible() && missed) { - val caller = Utils.friendlyUri(ContactsActivity.contactName(call.peerUri), - Utils.aorDomain(aor)) - val intent = Intent(applicationContext, MainActivity::class.java) - intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or - Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK - intent.putExtra("action", "call missed") - .putExtra("uap", uap) - val pi = if (VERSION.SDK_INT >= 23) - PendingIntent.getActivity(applicationContext, CALL_REQ_CODE, intent, - PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) - else - PendingIntent.getActivity(applicationContext, CALL_REQ_CODE, intent, - PendingIntent.FLAG_UPDATE_CURRENT) - val nb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID) - nb.setSmallIcon(R.drawable.ic_stat_phone_missed) - .setColor(ContextCompat.getColor(this, R.color.colorBaresip)) - .setContentIntent(pi) - .setCategory(Notification.CATEGORY_CALL) - .setAutoCancel(true) - if (VERSION.SDK_INT < 23) { - nb.setContentTitle(getString(R.string.missed_call_from)) - nb.setContentText(caller) - } else { - var missedCalls = 0 - for (notification in nm.activeNotifications) - if (notification.id == CALL_MISSED_NOTIFICATION_ID) - missedCalls++ - if (missedCalls == 0) { + if (!Utils.isVisible()) { + if (missed) { + val caller = Utils.friendlyUri(ContactsActivity.contactName(call.peerUri), + Utils.aorDomain(aor)) + val intent = Intent(applicationContext, MainActivity::class.java) + intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or + Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK + intent.putExtra("action", "call missed") + .putExtra("uap", uap) + val pi = if (VERSION.SDK_INT >= 23) + PendingIntent.getActivity(applicationContext, CALL_REQ_CODE, intent, + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) + else + PendingIntent.getActivity(applicationContext, CALL_REQ_CODE, intent, + PendingIntent.FLAG_UPDATE_CURRENT) + val nb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID) + nb.setSmallIcon(R.drawable.ic_stat_phone_missed) + .setColor(ContextCompat.getColor(this, R.color.colorBaresip)) + .setContentIntent(pi) + .setCategory(Notification.CATEGORY_CALL) + .setAutoCancel(true) + if (VERSION.SDK_INT < 23) { nb.setContentTitle(getString(R.string.missed_call_from)) nb.setContentText(caller) } else { - nb.setContentTitle(getString(R.string.missed_calls)) - nb.setContentText( - String.format(getString(R.string.missed_calls_count), - missedCalls + 1)) + var missedCalls = 0 + for (notification in nm.activeNotifications) + if (notification.id == CALL_MISSED_NOTIFICATION_ID) + missedCalls++ + if (missedCalls == 0) { + nb.setContentTitle(getString(R.string.missed_call_from)) + nb.setContentText(caller) + } else { + nb.setContentTitle(getString(R.string.missed_calls)) + nb.setContentText( + String.format(getString(R.string.missed_calls_count), + missedCalls + 1)) + } } + if (VERSION.SDK_INT < 26) { + @Suppress("DEPRECATION") + nb.setVibrate(LongArray(0)) + .setVisibility(NotificationCompat.VISIBILITY_PRIVATE) + .priority = Notification.PRIORITY_HIGH + } + nm.notify(CALL_MISSED_NOTIFICATION_ID, nb.build()) } - if (VERSION.SDK_INT < 26) { - @Suppress("DEPRECATION") - nb.setVibrate(LongArray(0)) - .setVisibility(NotificationCompat.VISIBILITY_PRIVATE) - .priority = Notification.PRIORITY_HIGH - } - nm.notify(CALL_MISSED_NOTIFICATION_ID, nb.build()) return } + val reason = ev[1].trim() + if ((reason != "") && (Call.uaCalls(ua, "").size == 0)) { + if (reason[0].isDigit()) + toast("${getString(R.string.call_failed)}: $reason") + else + toast("${getString(R.string.call_closed)}: $reason") + } } } } } - if (newEvent == null) newEvent = event - val intent = Intent("service event") - intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP - intent.putExtra("event", newEvent) - intent.putExtra("params", arrayListOf(uap, callp)) - LocalBroadcastManager.getInstance(this).sendBroadcast(intent) + serviceEvent.postValue(Event(ServiceEvent(newEvent ?: event, arrayListOf(uap, callp), + System.currentTimeMillis()))) } @SuppressLint("UnspecifiedImmutableFlag") @@ -886,9 +898,10 @@ class BaresipService: Service() { Log.w(TAG, "messageEvent did not find ua $uap") return } - val timeStamp = System.currentTimeMillis().toString() - Log.d(TAG, "Message event for $uap from $peer at $timeStamp") - Message(ua.account.aor, peer, text, timeStamp.toLong(), + val timeStamp = System.currentTimeMillis() + val timeStampString = timeStamp.toString() + Log.d(TAG, "Message event for $uap from $peer at $timeStampString") + Message(ua.account.aor, peer, text, timeStamp, R.drawable.arrow_down_green, 0, "", true).add() Message.save() ua.account.unreadMessages = true @@ -934,13 +947,13 @@ class BaresipService: Service() { val saveIntent = Intent(this, BaresipService::class.java) saveIntent.action = "Message Save" saveIntent.putExtra("uap", uap) - .putExtra("time", timeStamp) + .putExtra("time", timeStampString) val savePendingIntent = PendingIntent.getService(this, SAVE_REQ_CODE, saveIntent, PendingIntent.FLAG_UPDATE_CURRENT) val deleteIntent = Intent(this, BaresipService::class.java) deleteIntent.action = "Message Delete" deleteIntent.putExtra("uap", uap) - .putExtra("time", timeStamp) + .putExtra("time", timeStampString) val deletePendingIntent = PendingIntent.getService(this, DELETE_REQ_CODE, deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT) nb.addAction(R.drawable.ic_stat_reply, "Reply", rpi) @@ -950,11 +963,8 @@ class BaresipService: Service() { return } nt.play() - val intent = Intent("service event") - intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP - intent.putExtra("event", "message show") - intent.putExtra("params", arrayListOf(uap, peer)) - LocalBroadcastManager.getInstance(this).sendBroadcast(intent) + serviceEvent.postValue(Event(ServiceEvent("message show", arrayListOf(uap, peer), + timeStamp))) } @Keep @@ -993,10 +1003,8 @@ class BaresipService: Service() { fun started() { Log.d(TAG, "Received 'started' from baresip") Api.net_debug() - val intent = Intent("service event") - intent.putExtra("event", "started") - intent.putExtra("params", arrayListOf(callActionUri)) - LocalBroadcastManager.getInstance(this).sendBroadcast(intent) + serviceEvent.postValue(Event(ServiceEvent("started", arrayListOf(callActionUri), + System.currentTimeMillis()))) callActionUri = "" if (VERSION.SDK_INT >= 23) Log.d(TAG, "Battery optimizations are ignored: " + @@ -1009,10 +1017,8 @@ class BaresipService: Service() { fun stopped(error: String) { Log.d(TAG, "Received 'stopped' from baresip with param '$error'") isServiceRunning = false - val intent = Intent("service event") - intent.putExtra("event", "stopped") - intent.putExtra("params", arrayListOf(error)) - LocalBroadcastManager.getInstance(this).sendBroadcast(intent) + serviceEvent.postValue(Event(ServiceEvent("stopped", arrayListOf(error), + System.currentTimeMillis()))) stopForeground(true) stopSelf() } @@ -1133,10 +1139,14 @@ class BaresipService: Service() { } } + @SuppressLint("MissingPermission") private fun isBluetoothHeadsetConnected(): Boolean { - return (btAdapter != null) && btAdapter!!.isEnabled && - (btAdapter!!.getProfileConnectionState(BluetoothHeadset.HEADSET) == - BluetoothHeadset.STATE_CONNECTED) + return if (VERSION.SDK_INT < 31) + btAdapter != null && btAdapter!!.isEnabled && + btAdapter!!.getProfileConnectionState(BluetoothHeadset.HEADSET) == BluetoothHeadset.STATE_CONNECTED + else + // getProfileConnectionState requires asking fot BLUETOOTH_CONNECT permission + true } private fun isAudioFocused(): Boolean { @@ -1447,18 +1457,14 @@ class BaresipService: Service() { val activities = mutableListOf() var dnsServers = listOf() + var serviceEvent = MutableLiveData>() } init { - - messageUpdate.postValue(System.currentTimeMillis()) - registrationUpdate.postValue(System.currentTimeMillis()) - if (!libraryLoaded) { Log.d(TAG, "Loading baresip library") System.loadLibrary("baresip") libraryLoaded = true } - } } diff --git a/app/src/main/kotlin/com/tutpro/baresip/Event.kt b/app/src/main/kotlin/com/tutpro/baresip/Event.kt new file mode 100644 index 00000000..1fa00df1 --- /dev/null +++ b/app/src/main/kotlin/com/tutpro/baresip/Event.kt @@ -0,0 +1,15 @@ +package com.tutpro.baresip + +import java.util.concurrent.atomic.AtomicBoolean + +open class Event(private val content: T) { + private val hasBeenHandled = AtomicBoolean(false) + fun getContentIfNotHandled(): T? { + return if (hasBeenHandled.get()) { + null + } else { + hasBeenHandled.set(true) + content + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt index ca6b655f..f3796be7 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt @@ -29,7 +29,6 @@ import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat -import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.lifecycle.Observer import com.google.android.material.snackbar.Snackbar @@ -39,7 +38,6 @@ import java.io.File import java.io.FileInputStream import java.io.FileOutputStream import java.net.URLDecoder -import java.util.* import kotlin.collections.ArrayList import kotlin.system.exitProcess import android.content.IntentFilter @@ -73,8 +71,8 @@ class MainActivity : AppCompatActivity() { private lateinit var nm: NotificationManager private lateinit var am: AudioManager private lateinit var kgm: KeyguardManager - private lateinit var serviceEventReceiver: BroadcastReceiver private lateinit var screenEventReceiver: BroadcastReceiver + private lateinit var serviceEventObserver: Observer> private lateinit var quitTimer: CountDownTimer private lateinit var stopState: String private var micIcon: MenuItem? = null @@ -116,7 +114,7 @@ class MainActivity : AppCompatActivity() { binding = ActivityMainBinding.inflate(layoutInflater) val extraAction = intent.getStringExtra("action") - Log.d(TAG, "MainActivity onCreate ${intent.action}/${intent.data}/$extraAction") + Log.d(TAG, "Main onCreate ${intent.action}/${intent.data}/$extraAction") if (intent?.action == ACTION_CALL && !BaresipService.isServiceRunning) BaresipService.callActionUri = URLDecoder.decode(intent.data.toString(), "UTF-8") @@ -159,15 +157,15 @@ class MainActivity : AppCompatActivity() { am = getSystemService(AUDIO_SERVICE) as AudioManager kgm = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager - serviceEventReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - handleServiceEvent(intent.getStringExtra("event")!!, - intent.getStringArrayListExtra("params")!!) + serviceEventObserver = Observer> { + val serviceEvent = it.getContentIfNotHandled() + if (serviceEvent != null) { + Log.d(TAG, "Observed event ${serviceEvent.event}") + handleServiceEvent(serviceEvent.event, serviceEvent.params) } } - LocalBroadcastManager.getInstance(this).registerReceiver(serviceEventReceiver, - IntentFilter("service event")) + BaresipService.serviceEvent.observeForever(serviceEventObserver) screenEventReceiver = object : BroadcastReceiver() { override fun onReceive(contxt: Context, intent: Intent) { @@ -729,7 +727,7 @@ class MainActivity : AppCompatActivity() { super.onDestroy() Log.d(TAG, "Main onDestroy") this.unregisterReceiver(screenEventReceiver) - LocalBroadcastManager.getInstance(this).unregisterReceiver(serviceEventReceiver) + BaresipService.serviceEvent.removeObserver(serviceEventObserver) BaresipService.activities.clear() } @@ -960,8 +958,9 @@ class MainActivity : AppCompatActivity() { showCall(ua, Call.ofCallp(callp)) } else { Log.d(TAG, "Reordering to front") + BaresipService.activities.clear() val i = Intent(applicationContext, MainActivity::class.java) - i.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK + i.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP i.putExtra("action", "call show") i.putExtra("callp", callp) startActivity(i) @@ -1040,11 +1039,17 @@ class MainActivity : AppCompatActivity() { } "call transfer", "transfer show" -> { val callp = params[1] - val call = Call.ofCallp(callp) - if (call == null) { - Log.w(TAG, "Call $callp to be transferred is not found") + if (!BaresipService.isMainVisible) { + Log.d(TAG, "Reordering to front") + BaresipService.activities.clear() + val i = Intent(applicationContext, MainActivity::class.java) + i.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP + i.putExtra("action", event) + i.putExtra("callp", callp) + startActivity(i) return } + val call = Call.ofCallp(callp)!! val titleView = View.inflate(this, R.layout.alert_title, null) as TextView titleView.text = getString(R.string.transfer_request) val target = Utils.friendlyUri(ContactsActivity.contactName(ev[1]), @@ -1069,19 +1074,12 @@ class MainActivity : AppCompatActivity() { "transfer accept" -> { val callp = params[1] val call = Call.ofCallp(callp) - if (call == null) { - Log.w(TAG, "Call $callp to be transferred is not found") - return - } if (call in Call.calls()) Api.ua_hangup(uap, callp, 0, "") call(ua, ev[1]) showCall(ua) } "transfer failed" -> { - Toast.makeText(applicationContext, - "${getString(R.string.transfer_failed)}: ${ev[1].trim()}", - Toast.LENGTH_LONG).show() showCall(ua) } "call closed" -> { @@ -1097,18 +1095,6 @@ class MainActivity : AppCompatActivity() { } if (speakerIcon != null) speakerIcon!!.setIcon(R.drawable.speaker_off) - val param = ev[1].trim() - if ((param != "") && (Call.uaCalls(ua, "").size == 0)) { - if (param[0].isDigit()) - Toast.makeText(applicationContext, - "${getString(R.string.call_failed)}: $param", - Toast.LENGTH_LONG).show() - else - Toast.makeText(applicationContext, - "${getString(R.string.call_closed)}: $param", - Toast.LENGTH_LONG).show() - } - restoreActivities() if ((Build.VERSION.SDK_INT >= 22 && kgm.isDeviceLocked) || (Build.VERSION.SDK_INT < 22 && kgm.isKeyguardLocked && kgm.isKeyguardSecure)) Utils.setShowWhenLocked(this, false) diff --git a/app/src/main/kotlin/com/tutpro/baresip/ServiceEvent.kt b/app/src/main/kotlin/com/tutpro/baresip/ServiceEvent.kt new file mode 100644 index 00000000..58a2f761 --- /dev/null +++ b/app/src/main/kotlin/com/tutpro/baresip/ServiceEvent.kt @@ -0,0 +1,3 @@ +package com.tutpro.baresip + +class ServiceEvent (val event: String, val params: ArrayList, val timeStamp: Long) \ No newline at end of file