Allow linphone-config URIs in QR codes scanned inside Linphone

This commit is contained in:
Sylvain Berfini 2025-12-11 10:25:46 +01:00
parent 618be9ee7c
commit d2b12159af
3 changed files with 46 additions and 24 deletions

View file

@ -19,7 +19,6 @@
*/ */
package org.linphone.ui.assistant.viewmodel package org.linphone.ui.assistant.viewmodel
import android.util.Patterns
import androidx.annotation.UiThread import androidx.annotation.UiThread
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
@ -32,6 +31,7 @@ import org.linphone.ui.GenericViewModel
import org.linphone.utils.Event import org.linphone.utils.Event
import org.linphone.R import org.linphone.R
import org.linphone.core.GlobalState import org.linphone.core.GlobalState
import org.linphone.utils.LinphoneUtils
class QrCodeViewModel class QrCodeViewModel
@UiThread @UiThread
@ -76,30 +76,31 @@ class QrCodeViewModel
if (result == null) { if (result == null) {
showRedToast(R.string.assistant_qr_code_invalid_toast, R.drawable.warning_circle) showRedToast(R.string.assistant_qr_code_invalid_toast, R.drawable.warning_circle)
} else { } else {
val isValidUrl = Patterns.WEB_URL.matcher(result).matches() val url = LinphoneUtils.getRemoteProvisioningUrlFromUri(result)
if (!isValidUrl) { if (url == null) {
Log.e("$TAG The content of the QR Code doesn't seem to be a valid web URL") Log.e("$TAG The content of the QR Code [$result] doesn't seem to be a valid web URL")
showRedToast(R.string.assistant_qr_code_invalid_toast, R.drawable.warning_circle) showRedToast(R.string.assistant_qr_code_invalid_toast, R.drawable.warning_circle)
} else { return
}
Log.i( Log.i(
"$TAG QR code URL set, restarting the Core outside of iterate() loop to apply configuration changes" "$TAG Setting QR code URL [$url], restarting the Core outside of iterate() loop to apply configuration changes"
) )
core.nativePreviewWindowId = null core.nativePreviewWindowId = null
core.isVideoPreviewEnabled = false core.isVideoPreviewEnabled = false
core.isQrcodeVideoPreviewEnabled = false core.isQrcodeVideoPreviewEnabled = false
core.provisioningUri = result core.provisioningUri = url
coreContext.postOnCoreThread { core -> coreContext.postOnCoreThread { core ->
Log.i("$TAG Stopping Core") Log.i("$TAG Stopping Core")
coreContext.core.stop() core.stop()
Log.i("$TAG Core has been stopped, restarting it") Log.i("$TAG Core has been stopped, restarting it")
coreContext.core.start() core.start()
Log.i("$TAG Core has been restarted") Log.i("$TAG Core has been restarted")
} }
} }
} }
} }
}
init { init {
coreContext.postOnCoreThread { core -> coreContext.postOnCoreThread { core ->

View file

@ -790,11 +790,11 @@ class MainActivity : GenericActivity() {
} }
private fun handleConfigIntent(uri: String) { private fun handleConfigIntent(uri: String) {
val remoteConfigUri = uri.substring("linphone-config:".length) Log.i("$TAG Trying to parse config intent [$uri] as remote provisioning URL")
val url = when { val url = LinphoneUtils.getRemoteProvisioningUrlFromUri(uri)
remoteConfigUri.startsWith("http://") || remoteConfigUri.startsWith("https://") -> remoteConfigUri if (url == null) {
remoteConfigUri.startsWith("file://") -> remoteConfigUri Log.e("$TAG Couldn't parse URI [$uri] into a valid remote provisioning URL, aborting")
else -> "https://$remoteConfigUri" return
} }
coreContext.postOnCoreThread { core -> coreContext.postOnCoreThread { core ->

View file

@ -23,6 +23,7 @@ import android.graphics.Typeface
import android.text.Spannable import android.text.Spannable
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.text.style.StyleSpan import android.text.style.StyleSpan
import android.util.Patterns
import androidx.annotation.AnyThread import androidx.annotation.AnyThread
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.annotation.IntegerRes import androidx.annotation.IntegerRes
@ -65,6 +66,26 @@ class LinphoneUtils {
const val RECORDING_MKV_FILE_EXTENSION = ".mkv" const val RECORDING_MKV_FILE_EXTENSION = ".mkv"
const val RECORDING_SMFF_FILE_EXTENSION = ".smff" const val RECORDING_SMFF_FILE_EXTENSION = ".smff"
@AnyThread
fun getRemoteProvisioningUrlFromUri(uri: String): String? {
val linphoneScheme = "linphone-config:"
return if (uri.startsWith(linphoneScheme)) {
val remoteConfigUri = uri.substring(linphoneScheme.length)
val url = when {
remoteConfigUri.startsWith("http://") || remoteConfigUri.startsWith("https://") -> remoteConfigUri
remoteConfigUri.startsWith("file://") -> remoteConfigUri
else -> "https://$remoteConfigUri"
}
url
} else {
val isValidUrl = Patterns.WEB_URL.matcher(uri).matches()
if (!isValidUrl) {
return null
}
uri
}
}
@WorkerThread @WorkerThread
fun getDefaultAccount(): Account? { fun getDefaultAccount(): Account? {
return coreContext.core.defaultAccount ?: coreContext.core.accountList.firstOrNull() return coreContext.core.defaultAccount ?: coreContext.core.accountList.firstOrNull()