diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 176bd86c..f62d3ba9 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
+ xmlns:tools="http://schemas.android.com/tools" >
@@ -15,8 +15,7 @@
-
@@ -44,6 +43,7 @@
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:remove="android:appComponentFactory" >
+
+
+
+
+
+
@@ -63,26 +68,29 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ tools:ignore="ExportedReceiver" >
@@ -173,4 +209,4 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/tutpro/baresip/ConfigActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/ConfigActivity.kt
index 8176cfb7..af2e345f 100644
--- a/app/src/main/kotlin/com/tutpro/baresip/ConfigActivity.kt
+++ b/app/src/main/kotlin/com/tutpro/baresip/ConfigActivity.kt
@@ -2,6 +2,7 @@ package com.tutpro.baresip
import android.Manifest
import android.app.Activity
+import android.app.role.RoleManager
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
@@ -21,6 +22,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
+import androidx.transition.Visibility
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import com.tutpro.baresip.Utils.copyInputStreamToFile
@@ -36,7 +38,6 @@ class ConfigActivity : AppCompatActivity() {
private lateinit var layout: ScrollView
private lateinit var baresipService: Intent
private lateinit var autoStart: CheckBox
- private lateinit var batteryOptimizations: CheckBox
private lateinit var listenAddr: EditText
private lateinit var netAfSpinner: Spinner
private lateinit var netAf: String
@@ -49,6 +50,8 @@ class ConfigActivity : AppCompatActivity() {
private lateinit var contactsSpinner: Spinner
private lateinit var contactsMode: String
private lateinit var contactsModeKeys: ArrayList
+ private lateinit var batteryOptimizations: CheckBox
+ private lateinit var defaultDialer: CheckBox
private lateinit var debug: CheckBox
private lateinit var sipTrace: CheckBox
private lateinit var reset: CheckBox
@@ -74,6 +77,7 @@ class ConfigActivity : AppCompatActivity() {
goBack()
}
}
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -109,6 +113,37 @@ class ConfigActivity : AppCompatActivity() {
}
}
+ if (Build.VERSION.SDK_INT >= 29) {
+ val roleManager = getSystemService(ROLE_SERVICE) as RoleManager
+ defaultDialer = binding.DefaultPhoneApp
+ defaultDialer.isChecked = roleManager.isRoleHeld(RoleManager.ROLE_DIALER)
+ val dialerRoleRequest = registerForActivityResult(
+ ActivityResultContracts.StartActivityForResult()
+ ) {
+ Log.d(TAG, "dialerRoleRequest succeeded: ${it.resultCode == Activity.RESULT_OK}")
+ defaultDialer.isChecked = roleManager.isRoleHeld(RoleManager.ROLE_DIALER)
+ }
+ defaultDialer.setOnCheckedChangeListener { _, isChecked ->
+ if (isChecked) {
+ if (!roleManager.isRoleAvailable(RoleManager.ROLE_DIALER))
+ Utils.alertView(this, getString(R.string.notice),
+ getString(R.string.dialer_role_not_available))
+ else
+ if (!roleManager.isRoleHeld(RoleManager.ROLE_DIALER))
+ dialerRoleRequest.launch(roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER))
+ } else {
+ try {
+ dialerRoleRequest.launch(Intent("android.settings.MANAGE_DEFAULT_APPS_SETTINGS"))
+ } catch (e: ActivityNotFoundException) {
+ Log.e(TAG, "ActivityNotFound exception: $e")
+
+ }
+ }
+ }
+ } else {
+ binding.PhoneApp.visibility = View.GONE
+ }
+
listenAddr = binding.ListenAddress
val laCv = Config.variable("sip_listen")
oldListenAddr = if (laCv.size == 0) "" else laCv[0]
@@ -642,10 +677,6 @@ class ConfigActivity : AppCompatActivity() {
Utils.alertView(this, getString(R.string.start_automatically),
getString(R.string.start_automatically_help))
}
- binding.BatteryOptimizationsTitle.setOnClickListener {
- Utils.alertView(this, getString(R.string.battery_optimizations),
- getString(R.string.battery_optimizations_help))
- }
binding.ListenAddressTitle.setOnClickListener {
Utils.alertView(this, getString(R.string.listen_address),
getString(R.string.listen_address_help))
@@ -681,6 +712,14 @@ class ConfigActivity : AppCompatActivity() {
Utils.alertView(this, getString(R.string.contacts),
getString(R.string.contacts_help))
}
+ binding.BatteryOptimizationsTitle.setOnClickListener {
+ Utils.alertView(this, getString(R.string.battery_optimizations),
+ getString(R.string.battery_optimizations_help))
+ }
+ binding.DefaultPhoneAppTitle.setOnClickListener {
+ Utils.alertView(this, getString(R.string.default_phone_app),
+ getString(R.string.default_phone_app_help))
+ }
binding.DebugTitle.setOnClickListener {
Utils.alertView(this, getString(R.string.debug), getString(R.string.debug_help))
}
diff --git a/app/src/main/kotlin/com/tutpro/baresip/InCallService.kt b/app/src/main/kotlin/com/tutpro/baresip/InCallService.kt
new file mode 100644
index 00000000..fd31969e
--- /dev/null
+++ b/app/src/main/kotlin/com/tutpro/baresip/InCallService.kt
@@ -0,0 +1,14 @@
+package com.tutpro.baresip
+
+import android.app.Service
+import android.content.Intent
+import android.os.IBinder
+
+// This is needed in order to allow choosing baresip as default Phone app
+
+class InCallService : Service() {
+
+ override fun onBind(intent: Intent): IBinder {
+ TODO("Return the communication channel to the service.")
+ }
+}
\ 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 3658fdf9..cba2e54b 100644
--- a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt
+++ b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt
@@ -9,6 +9,7 @@ import android.app.NotificationManager
import android.content.*
import android.content.Intent.ACTION_CALL
import android.content.Intent.ACTION_DIAL
+import android.content.Intent.ACTION_VIEW
import android.content.pm.PackageManager
import android.content.res.Configuration.ORIENTATION_PORTRAIT
import android.media.AudioManager
@@ -37,7 +38,6 @@ import com.google.android.material.snackbar.Snackbar
import com.tutpro.baresip.Utils.showSnackBar
import com.tutpro.baresip.databinding.ActivityMainBinding
import java.io.File
-import java.net.URLDecoder
import kotlin.system.exitProcess
class MainActivity : AppCompatActivity() {
@@ -653,11 +653,13 @@ class MainActivity : AppCompatActivity() {
atStartup = intent.hasExtra("onStartup")
- if (intent?.action == ACTION_CALL || intent?.action == ACTION_DIAL) {
- if (BaresipService.isServiceRunning)
- callAction(intent)
- else
- BaresipService.callActionUri = URLDecoder.decode(intent.data.toString(), "UTF-8")
+ when (intent?.action) {
+ ACTION_DIAL, ACTION_CALL, ACTION_VIEW ->
+ if (BaresipService.isServiceRunning)
+ callAction(intent.data, if (intent?.action == ACTION_CALL) "call" else "dial")
+ else
+ BaresipService.callActionUri = intent.data.toString()
+ .replace("tel:%2B", "tel:+")
}
permissions = if (Build.VERSION.SDK_INT >= 33)
@@ -756,6 +758,9 @@ class MainActivity : AppCompatActivity() {
callUri.setText(BaresipService.uas[aorSpinner.selectedItemPosition].account.resumeUri)
callButton.performClick()
}
+ "dial" -> {
+ callUri.setText(BaresipService.uas[aorSpinner.selectedItemPosition].account.resumeUri)
+ }
"call transfer", "transfer show", "transfer accept" ->
handleServiceEvent("$resumeAction,$resumeUri",
arrayListOf(resumeCall!!.ua.uap, resumeCall!!.callp))
@@ -824,11 +829,11 @@ class MainActivity : AppCompatActivity() {
resumeAction = ""
resumeUri = ""
- Log.d(TAG, "onNewIntent with intent.action '${intent.action}'")
+ Log.d(TAG, "onNewIntent with action/data '${intent.action}/${intent.data}'")
when (intent.action) {
- ACTION_DIAL, ACTION_CALL -> {
- callAction(intent)
+ ACTION_DIAL, ACTION_CALL, ACTION_VIEW -> {
+ callAction(intent.data, if (intent.action == ACTION_CALL) "call" else "dial")
}
else -> {
val action = intent.getStringExtra("action")
@@ -840,15 +845,14 @@ class MainActivity : AppCompatActivity() {
}
}
- private fun callAction(intent: Intent) {
+ private fun callAction(uri: Uri?, action: String) {
if (Call.inCall() || BaresipService.uas.size == 0)
return
- val uri: Uri? = intent.data
- Log.d(TAG, "callAction to $uri")
+ Log.d(TAG, "Action $action to $uri")
if (uri != null) {
when (uri.scheme) {
"sip" -> {
- val uriStr = URLDecoder.decode(uri.toString(), "UTF-8")
+ val uriStr = uri.toString()
var ua = UserAgent.ofDomain(Utils.uriHostPart(uriStr))
if (ua == null && BaresipService.uas.size > 0)
ua = BaresipService.uas[0]
@@ -857,11 +861,11 @@ class MainActivity : AppCompatActivity() {
return
}
spinToAor(ua.account.aor)
- resumeAction = "call"
+ resumeAction = action
ua.account.resumeUri = uriStr
}
"tel" -> {
- val uriStr = URLDecoder.decode(uri.toString(), "UTF-8")
+ val uriStr = uri.toString().replace("%2B", "+")
.filterNot{setOf('-', ' ', '(', ')').contains(it)}
var account: Account? = null
for (a in Account.accounts())
@@ -874,7 +878,7 @@ class MainActivity : AppCompatActivity() {
return
}
spinToAor(account.aor)
- resumeAction = "call"
+ resumeAction = action
account.resumeUri = uriStr
}
else -> {
@@ -897,7 +901,7 @@ class MainActivity : AppCompatActivity() {
getString(R.string.no_network))
return
}
- "call" -> {
+ "call", "dial" -> {
if (Call.inCall()) {
Toast.makeText(applicationContext, getString(R.string.call_already_active),
Toast.LENGTH_SHORT).show()
@@ -957,7 +961,7 @@ class MainActivity : AppCompatActivity() {
val uap = intent.getLongExtra("uap", 0L)
val ua = UserAgent.ofUap(uap)
if (ua == null) {
- Log.w(TAG, "onNewIntent did not find ua $uap")
+ Log.w(TAG, "handleIntent did not find ua $uap")
return
}
if (ua.account.aor != aorSpinner.tag)
@@ -1005,27 +1009,16 @@ class MainActivity : AppCompatActivity() {
}
if (event == "started") {
- val callActionUri = params[0] as String
- Log.d(TAG, "Handling service event 'started' with '$callActionUri'")
+ val uriString = params[0] as String
+ Log.d(TAG, "Handling service event 'started' with URI '$uriString'")
if (!this::uaAdapter.isInitialized) {
// Android has restarted baresip when permission has been denied in app settings
recreate()
return
}
uaAdapter.notifyDataSetChanged()
- if (callActionUri != "") {
- var ua = UserAgent.ofDomain(Utils.uriHostPart(callActionUri))
- if (ua == null)
- if (BaresipService.uas.size > 0) {
- ua = BaresipService.uas[0]
- } else {
- handleNextEvent("No UAs to make the call to '$callActionUri'")
- return
- }
- spinToAor(ua.account.aor)
- ua.account.resumeUri = callActionUri
- callUri.setText(callActionUri)
- callButton.performClick()
+ if (uriString != "") {
+ callAction(uriString.toUri(), "dial")
} else {
if ((aorSpinner.selectedItemPosition == -1) && (BaresipService.uas.size > 0)) {
aorSpinner.setSelection(0)
diff --git a/app/src/main/res/layout/activity_config.xml b/app/src/main/res/layout/activity_config.xml
index 5e40dc1c..85c2ffd1 100644
--- a/app/src/main/res/layout/activity_config.xml
+++ b/app/src/main/res/layout/activity_config.xml
@@ -41,33 +41,6 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Ota akun käytön optimointi pois päältä (suositeltu),
jos haluat vähentää todennäköisyyttä, että Android rajoittaa baresip-sovelluksen toimintaa
ja pääsyä verkkoon.
+ Oletuspuhelinsovellus
+ Puhelinrooli ei ole saatavana
+ Jos merkity, baresip on oletuspuhelinsovellus. Älä
+ merkitse, jos laitteesi täytyy hallita myös muita kuin SIP-puheluita tai -viestejä.
Kuunteluosoite
IP-osoite ja portti muotoa
\'osoite:portti\', missä baresip kuuntelee sisään tulevia
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b5ae1e9a..7cf0501c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -297,6 +297,10 @@
Disable battery optimizations (recommended) if you want
to reduce likelihood that Android restricts baresip\'s access to network or enters baresip
to standby state.
+ Default Phone App
+ Dialer role is not available
+ If checked, baresip is the default phone app. Do not check
+ if your device may need to handle also other that SIP calls or messages.
Listen Address
IP address and port of form \'address:port\' at which baresip listens
for incoming SIP requests. If IP address is an IPv6 address, it must be written inside