diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c1b106232..7df1fe3ed 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -217,6 +217,15 @@
+
+
+
+
+
+
= android.os.Build.VERSION_CODES.Q) {
+ val roleManager: RoleManager = getSystemService(Context.ROLE_SERVICE) as RoleManager
+ // Check if the app needs to register call redirection role.
+ val shouldRequestRole = roleManager.isRoleAvailable(RoleManager.ROLE_CALL_REDIRECTION) &&
+ !roleManager.isRoleHeld(RoleManager.ROLE_CALL_REDIRECTION)
+ if (shouldRequestRole) {
+ val intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_REDIRECTION)
+ startActivityForResult(intent, 123456)
+ }
+ }
+ }
}
diff --git a/app/src/main/java/org/linphone/telecom/TelecomConnectionService.kt b/app/src/main/java/org/linphone/telecom/TelecomConnectionService.kt
index fddfc8bb6..06b81ab87 100644
--- a/app/src/main/java/org/linphone/telecom/TelecomConnectionService.kt
+++ b/app/src/main/java/org/linphone/telecom/TelecomConnectionService.kt
@@ -104,8 +104,17 @@ class TelecomConnectionService : ConnectionService() {
request: ConnectionRequest
): Connection {
if (coreContext.core.callsNb == 0) {
- Log.w("[Telecom Connection Service] No call in Core, aborting outgoing connection!")
- return Connection.createCanceledConnection()
+ val uri = request.address
+ val address = coreContext.core.interpretUrl(uri.toString(), true)
+ if (address != null) {
+ Log.i(
+ "[Telecom Connection Service] Starting call to [${address.asStringUriOnly()}]"
+ )
+ coreContext.startCall(address = address)
+ } else {
+ Log.w("[Telecom Connection Service] No call in Core, aborting outgoing connection!")
+ return Connection.createCanceledConnection()
+ }
}
val accountHandle = request.accountHandle
diff --git a/app/src/main/java/org/linphone/telecom/TelecomRedirectionService.kt b/app/src/main/java/org/linphone/telecom/TelecomRedirectionService.kt
new file mode 100644
index 000000000..43da38816
--- /dev/null
+++ b/app/src/main/java/org/linphone/telecom/TelecomRedirectionService.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2010-2025 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-android
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.linphone.telecom
+
+import android.net.Uri
+import android.os.Build
+import android.telecom.CallRedirectionService
+import android.telecom.PhoneAccountHandle
+import androidx.annotation.RequiresApi
+import androidx.core.net.toUri
+import org.linphone.LinphoneApplication.Companion.coreContext
+import org.linphone.core.tools.Log
+
+@RequiresApi(Build.VERSION_CODES.Q)
+class TelecomRedirectionService : CallRedirectionService() {
+ companion object {
+ private const val TAG = "[Telecom Redirection Service]"
+ }
+
+ override fun onPlaceCall(
+ handle: Uri,
+ initialPhoneAccount: PhoneAccountHandle,
+ allowInteractiveResponse: Boolean
+ ) {
+ Log.i("$TAG onPlaceCall called with URI [$handle]")
+
+ if (handle.scheme != "tel") {
+ placeCallUnmodified()
+ return
+ }
+
+ val number = handle.toString().substring("tel:".length)
+ Log.i("$TAG Extracted number [$number] from tel: URI")
+
+ if (TelecomHelper.exists()) {
+ val telecomHelper = TelecomHelper.get()
+ val phoneAccount = telecomHelper.findExistingAccount(coreContext.context)
+ val handle = phoneAccount?.accountHandle
+ if (handle != null) {
+ redirectCall(
+ "sip:sylvain@sip.linphone.org".toUri(),
+ handle,
+ allowInteractiveResponse
+ )
+ } else {
+ placeCallUnmodified()
+ }
+ } else {
+ placeCallUnmodified()
+ }
+ }
+}