Browse Source

Merge pull request #1477 from vector-im/feature/bma/elementCallUrlParam

Element call url param
pull/1479/head
Benoit Marty 12 months ago committed by GitHub
parent
commit
181a7b797c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 37
      features/call/src/main/kotlin/io/element/android/features/call/CallIntentDataParser.kt
  2. 190
      features/call/src/test/kotlin/io/element/android/features/call/CallIntentDataParserTests.kt

37
features/call/src/main/kotlin/io/element/android/features/call/CallIntentDataParser.kt

@ -54,21 +54,50 @@ class CallIntentDataParser @Inject constructor() {
} }
/** /**
* Ensure the uri has the following parameters and value: * Ensure the uri has the following parameters and value in the fragment:
* - appPrompt=false * - appPrompt=false
* - confineToRoom=true * - confineToRoom=true
* to ensure that the rendering will bo correct on the embedded Webview. * to ensure that the rendering will bo correct on the embedded Webview.
*/ */
private fun Uri.withCustomParameters(): String { private fun Uri.withCustomParameters(): String {
val builder = buildUpon() val builder = buildUpon()
// Remove the existing query parameters
builder.clearQuery() builder.clearQuery()
queryParameterNames.forEach { queryParameterNames.forEach {
if (it == APP_PROMPT_PARAMETER || it == CONFINE_TO_ROOM_PARAMETER) return@forEach if (it == APP_PROMPT_PARAMETER || it == CONFINE_TO_ROOM_PARAMETER) return@forEach
builder.appendQueryParameter(it, getQueryParameter(it)) builder.appendQueryParameter(it, getQueryParameter(it))
} }
builder.appendQueryParameter(APP_PROMPT_PARAMETER, "false") // Remove the existing fragment parameters, and build the new fragment
builder.appendQueryParameter(CONFINE_TO_ROOM_PARAMETER, "true") val currentFragment = fragment ?: ""
return builder.build().toString() // Reset the current fragment
builder.fragment("")
val queryFragmentPosition = currentFragment.lastIndexOf("?")
val newFragment = if (queryFragmentPosition == -1) {
// No existing query, build it.
"$currentFragment?$APP_PROMPT_PARAMETER=false&$CONFINE_TO_ROOM_PARAMETER=true"
} else {
buildString {
append(currentFragment.substring(0, queryFragmentPosition + 1))
val queryFragment = currentFragment.substring(queryFragmentPosition + 1)
// Replace the existing parameters
val newQueryFragment = queryFragment
.replace("$APP_PROMPT_PARAMETER=true", "$APP_PROMPT_PARAMETER=false")
.replace("$CONFINE_TO_ROOM_PARAMETER=false", "$CONFINE_TO_ROOM_PARAMETER=true")
append(newQueryFragment)
// Ensure the parameters are there
if (!newQueryFragment.contains("$APP_PROMPT_PARAMETER=false")) {
if (newQueryFragment.isNotEmpty()) {
append("&")
}
append("$APP_PROMPT_PARAMETER=false")
}
if (!newQueryFragment.contains("$CONFINE_TO_ROOM_PARAMETER=true")) {
append("&$CONFINE_TO_ROOM_PARAMETER=true")
}
}
}
// We do not want to encode the Fragment part, so append it manually
return builder.build().toString() + "#" + newFragment
} }
private const val APP_PROMPT_PARAMETER = "appPrompt" private const val APP_PROMPT_PARAMETER = "appPrompt"

190
features/call/src/test/kotlin/io/element/android/features/call/CallIntentDataParserTests.kt

@ -35,150 +35,188 @@ class CallIntentDataParserTests {
@Test @Test
fun `empty data returns null`() { fun `empty data returns null`() {
val url = "" doTest("", null)
assertThat(callIntentDataParser.parse(url)).isNull()
} }
@Test @Test
fun `invalid data returns null`() { fun `invalid data returns null`() {
val url = "!" doTest("!", null)
assertThat(callIntentDataParser.parse(url)).isNull()
} }
@Test @Test
fun `data with no scheme returns null`() { fun `data with no scheme returns null`() {
val url = "test" doTest("test", null)
assertThat(callIntentDataParser.parse(url)).isNull()
} }
@Test @Test
fun `Element Call http urls returns null`() { fun `Element Call http urls returns null`() {
val httpBaseUrl = "http://call.element.io" doTest("http://call.element.io", null)
val httpCallUrl = "http://call.element.io/some-actual-call?with=parameters" doTest("http://call.element.io/some-actual-call?with=parameters", null)
assertThat(callIntentDataParser.parse(httpBaseUrl)).isNull()
assertThat(callIntentDataParser.parse(httpCallUrl)).isNull()
} }
@Test @Test
fun `Element Call urls will be returned as is`() { fun `Element Call urls will be returned as is`() {
val httpsBaseUrl = "https://call.element.io" doTest(
val httpsCallUrl = VALID_CALL_URL_WITH_PARAM url = "https://call.element.io",
assertThat(callIntentDataParser.parse(httpsBaseUrl)).isEqualTo("$httpsBaseUrl?$EXTRA_PARAMS") expectedResult = "https://call.element.io#?$EXTRA_PARAMS"
assertThat(callIntentDataParser.parse(httpsCallUrl)).isEqualTo("$httpsCallUrl&$EXTRA_PARAMS") )
} }
@Test @Test
fun `HTTP and HTTPS urls that don't come from EC return null`() { fun `Element Call url with url param gets url extracted`() {
val httpBaseUrl = "http://app.element.io" doTest(
val httpsBaseUrl = "https://app.element.io" url = VALID_CALL_URL_WITH_PARAM,
val httpInvalidUrl = "http://" expectedResult = "$VALID_CALL_URL_WITH_PARAM#?$EXTRA_PARAMS"
val httpsInvalidUrl = "http://" )
assertThat(callIntentDataParser.parse(httpBaseUrl)).isNull()
assertThat(callIntentDataParser.parse(httpsBaseUrl)).isNull()
assertThat(callIntentDataParser.parse(httpInvalidUrl)).isNull()
assertThat(callIntentDataParser.parse(httpsInvalidUrl)).isNull()
} }
@Test @Test
fun `element scheme with call host and url with http will returns null`() { fun `HTTP and HTTPS urls that don't come from EC return null`() {
val embeddedUrl = "http://call.element.io/some-actual-call?with=parameters" doTest("http://app.element.io", null)
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8") doTest("https://app.element.io", null, testEmbedded = false)
val url = "element://call?url=$encodedUrl" doTest("http://", null)
assertThat(callIntentDataParser.parse(url)).isNull() doTest("https://", null)
} }
@Test @Test
fun `element scheme with call host and url param gets url extracted`() { fun `Element Call url with no url returns null`() {
val embeddedUrl = VALID_CALL_URL_WITH_PARAM val embeddedUrl = VALID_CALL_URL_WITH_PARAM
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8") val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
val url = "element://call?url=$encodedUrl" val url = "io.element.call:/?no_url=$encodedUrl"
assertThat(callIntentDataParser.parse(url)).isEqualTo("$VALID_CALL_URL_WITH_PARAM&$EXTRA_PARAMS") assertThat(callIntentDataParser.parse(url)).isNull()
} }
@Test @Test
fun `element scheme 2 with url param with http returns null`() { fun `element scheme with no call host returns null`() {
val embeddedUrl = "http://call.element.io/some-actual-call?with=parameters" val embeddedUrl = VALID_CALL_URL_WITH_PARAM
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8") val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
val url = "io.element.call:/?url=$encodedUrl" val url = "element://no-call?url=$encodedUrl"
assertThat(callIntentDataParser.parse(url)).isNull() assertThat(callIntentDataParser.parse(url)).isNull()
} }
@Test @Test
fun `element scheme 2 with url param gets url extracted`() { fun `element scheme with no data returns null`() {
val embeddedUrl = VALID_CALL_URL_WITH_PARAM val url = "element://call?url="
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8") assertThat(callIntentDataParser.parse(url)).isNull()
val url = "io.element.call:/?url=$encodedUrl"
assertThat(callIntentDataParser.parse(url)).isEqualTo("$VALID_CALL_URL_WITH_PARAM&$EXTRA_PARAMS")
} }
@Test @Test
fun `element scheme with call host and no url param returns null`() { fun `Element Call url with no data returns null`() {
val embeddedUrl = "http://call.element.io/some-actual-call?with=parameters" val url = "io.element.call:/?url="
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
val url = "element://call?no-url=$encodedUrl"
assertThat(callIntentDataParser.parse(url)).isNull() assertThat(callIntentDataParser.parse(url)).isNull()
} }
@Test @Test
fun `element scheme 2 with no url returns null`() { fun `element invalid scheme returns null`() {
val embeddedUrl = VALID_CALL_URL_WITH_PARAM val embeddedUrl = VALID_CALL_URL_WITH_PARAM
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8") val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
val url = "io.element.call:/?no_url=$encodedUrl" val url = "bad.scheme:/?url=$encodedUrl"
assertThat(callIntentDataParser.parse(url)).isNull() assertThat(callIntentDataParser.parse(url)).isNull()
} }
@Test @Test
fun `element scheme with no call host returns null`() { fun `Element Call url with url extra param appPrompt gets url extracted`() {
val embeddedUrl = VALID_CALL_URL_WITH_PARAM doTest(
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8") url = "${VALID_CALL_URL_WITH_PARAM}&appPrompt=true",
val url = "element://no-call?url=$encodedUrl" expectedResult = "$VALID_CALL_URL_WITH_PARAM#?$EXTRA_PARAMS"
assertThat(callIntentDataParser.parse(url)).isNull() )
} }
@Test @Test
fun `element scheme with no data returns null`() { fun `Element Call url with url extra param in fragment appPrompt gets url extracted`() {
val url = "element://call?url=" doTest(
assertThat(callIntentDataParser.parse(url)).isNull() url = "${VALID_CALL_URL_WITH_PARAM}#?appPrompt=true",
expectedResult = "$VALID_CALL_URL_WITH_PARAM#?appPrompt=false&confineToRoom=true"
)
} }
@Test @Test
fun `element scheme 2 with no data returns null`() { fun `Element Call url with url extra param in fragment appPrompt and other gets url extracted`() {
val url = "io.element.call:/?url=" doTest(
assertThat(callIntentDataParser.parse(url)).isNull() url = "${VALID_CALL_URL_WITH_PARAM}#?appPrompt=true&otherParam=maybe",
expectedResult = "$VALID_CALL_URL_WITH_PARAM#?appPrompt=false&otherParam=maybe&confineToRoom=true"
)
} }
@Test @Test
fun `element invalid scheme returns null`() { fun `Element Call url with url extra param confineToRoom gets url extracted`() {
val embeddedUrl = VALID_CALL_URL_WITH_PARAM doTest(
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8") url = "${VALID_CALL_URL_WITH_PARAM}&confineToRoom=false",
val url = "bad.scheme:/?url=$encodedUrl" expectedResult = "$VALID_CALL_URL_WITH_PARAM#?$EXTRA_PARAMS"
assertThat(callIntentDataParser.parse(url)).isNull() )
} }
@Test @Test
fun `element scheme 2 with url extra param appPrompt gets url extracted`() { fun `Element Call url with url extra param in fragment confineToRoom gets url extracted`() {
val embeddedUrl = "${VALID_CALL_URL_WITH_PARAM}&appPrompt=true" doTest(
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8") url = "${VALID_CALL_URL_WITH_PARAM}#?confineToRoom=false",
val url = "io.element.call:/?url=$encodedUrl" expectedResult = "$VALID_CALL_URL_WITH_PARAM#?confineToRoom=true&appPrompt=false"
assertThat(callIntentDataParser.parse(url)).isEqualTo("$VALID_CALL_URL_WITH_PARAM&$EXTRA_PARAMS") )
} }
@Test @Test
fun `element scheme 2 with url extra param confineToRoom gets url extracted`() { fun `Element Call url with url extra param in fragment confineToRoom and more gets url extracted`() {
val embeddedUrl = "${VALID_CALL_URL_WITH_PARAM}&confineToRoom=false" doTest(
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8") url = "${VALID_CALL_URL_WITH_PARAM}#?confineToRoom=false&otherParam=maybe",
val url = "io.element.call:/?url=$encodedUrl" expectedResult = "$VALID_CALL_URL_WITH_PARAM#?confineToRoom=true&otherParam=maybe&appPrompt=false"
assertThat(callIntentDataParser.parse(url)).isEqualTo("$VALID_CALL_URL_WITH_PARAM&$EXTRA_PARAMS") )
} }
@Test @Test
fun `element scheme 2 with url fragment gets url extracted`() { fun `Element Call url with url fragment gets url extracted`() {
val embeddedUrl = "${VALID_CALL_URL_WITH_PARAM}#fragment" doTest(
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8") url = "${VALID_CALL_URL_WITH_PARAM}#fragment",
val url = "io.element.call:/?url=$encodedUrl" expectedResult = "$VALID_CALL_URL_WITH_PARAM#fragment?$EXTRA_PARAMS"
assertThat(callIntentDataParser.parse(url)).isEqualTo("$VALID_CALL_URL_WITH_PARAM&$EXTRA_PARAMS#fragment") )
}
@Test
fun `Element Call url with url fragment with params gets url extracted`() {
doTest(
url = "${VALID_CALL_URL_WITH_PARAM}#fragment?otherParam=maybe",
expectedResult = "$VALID_CALL_URL_WITH_PARAM#fragment?otherParam=maybe&$EXTRA_PARAMS"
)
}
@Test
fun `Element Call url with url fragment with other params gets url extracted`() {
doTest(
url = "${VALID_CALL_URL_WITH_PARAM}#?otherParam=maybe",
expectedResult = "$VALID_CALL_URL_WITH_PARAM#?otherParam=maybe&$EXTRA_PARAMS"
)
}
@Test
fun `Element Call url with empty fragment`() {
doTest(
url = "${VALID_CALL_URL_WITH_PARAM}#",
expectedResult = "$VALID_CALL_URL_WITH_PARAM#?$EXTRA_PARAMS"
)
} }
@Test
fun `Element Call url with empty fragment query`() {
doTest(
url = "${VALID_CALL_URL_WITH_PARAM}#?",
expectedResult = "$VALID_CALL_URL_WITH_PARAM#?$EXTRA_PARAMS"
)
}
private fun doTest(url: String, expectedResult: String?, testEmbedded: Boolean = true) {
// Test direct parsing
assertThat(callIntentDataParser.parse(url)).isEqualTo(expectedResult)
if (testEmbedded) {
// Test embedded url, scheme 1
val encodedUrl = URLEncoder.encode(url, "utf-8")
val urlScheme1 = "element://call?url=$encodedUrl"
assertThat(callIntentDataParser.parse(urlScheme1)).isEqualTo(expectedResult)
// Test embedded url, scheme 2
val urlScheme2 = "io.element.call:/?url=$encodedUrl"
assertThat(callIntentDataParser.parse(urlScheme2)).isEqualTo(expectedResult)
}
}
companion object { companion object {
const val VALID_CALL_URL_WITH_PARAM = "https://call.element.io/some-actual-call?with=parameters" const val VALID_CALL_URL_WITH_PARAM = "https://call.element.io/some-actual-call?with=parameters"

Loading…
Cancel
Save