Skip to content

Commit 161c6d0

Browse files
authored
Merge pull request #1434 from vector-im/feature/bma/elementCallTweaks
Element call tweaks
2 parents 8e24d59 + 5d0751d commit 161c6d0

File tree

4 files changed

+93
-21
lines changed

4 files changed

+93
-21
lines changed

changelog.d/1434.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Element call: add custom parameters to Element Call urls.

features/call/src/main/AndroidManifest.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
<category android:name="android.intent.category.DEFAULT" />
4040
<category android:name="android.intent.category.BROWSABLE" />
4141

42-
<data android:scheme="http" />
4342
<data android:scheme="https" />
4443

4544
<data android:host="call.element.io" />

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

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ import javax.inject.Inject
2121

2222
class CallIntentDataParser @Inject constructor() {
2323

24-
private val validHttpSchemes = sequenceOf("http", "https")
24+
private val validHttpSchemes = sequenceOf("https")
2525

2626
fun parse(data: String?): String? {
2727
val parsedUrl = data?.let { Uri.parse(data) } ?: return null
2828
val scheme = parsedUrl.scheme
2929
return when {
30-
scheme in validHttpSchemes && parsedUrl.host == "call.element.io" -> data
30+
scheme in validHttpSchemes && parsedUrl.host == "call.element.io" -> parsedUrl
3131
scheme == "element" && parsedUrl.host == "call" -> {
3232
// We use this custom scheme to load arbitrary URLs for other instances of Element Call,
3333
// so we can only verify it's an HTTP/HTTPs URL with a non-empty host
@@ -40,14 +40,36 @@ class CallIntentDataParser @Inject constructor() {
4040
}
4141
// This should never be possible, but we still need to take into account the possibility
4242
else -> null
43-
}
43+
}?.withCustomParameters()
4444
}
4545

46-
private fun Uri.getUrlParameter(): String? {
46+
private fun Uri.getUrlParameter(): Uri? {
4747
return getQueryParameter("url")
48-
?.takeIf {
49-
val internalUri = Uri.parse(it)
50-
internalUri.scheme in validHttpSchemes && !internalUri.host.isNullOrBlank()
48+
?.let { urlParameter ->
49+
Uri.parse(urlParameter).takeIf { uri ->
50+
uri.scheme in validHttpSchemes && !uri.host.isNullOrBlank()
51+
}
5152
}
5253
}
5354
}
55+
56+
/**
57+
* Ensure the uri has the following parameters and value:
58+
* - appPrompt=false
59+
* - confineToRoom=true
60+
* to ensure that the rendering will bo correct on the embedded Webview.
61+
*/
62+
private fun Uri.withCustomParameters(): String {
63+
val builder = buildUpon()
64+
builder.clearQuery()
65+
queryParameterNames.forEach {
66+
if (it == APP_PROMPT_PARAMETER || it == CONFINE_TO_ROOM_PARAMETER) return@forEach
67+
builder.appendQueryParameter(it, getQueryParameter(it))
68+
}
69+
builder.appendQueryParameter(APP_PROMPT_PARAMETER, "false")
70+
builder.appendQueryParameter(CONFINE_TO_ROOM_PARAMETER, "true")
71+
return builder.build().toString()
72+
}
73+
74+
private const val APP_PROMPT_PARAMETER = "appPrompt"
75+
private const val CONFINE_TO_ROOM_PARAMETER = "confineToRoom"

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

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,19 @@ class CallIntentDataParserTests {
5252
}
5353

5454
@Test
55-
fun `Element Call urls will be returned as is`() {
55+
fun `Element Call http urls returns null`() {
5656
val httpBaseUrl = "http://call.element.io"
5757
val httpCallUrl = "http://call.element.io/some-actual-call?with=parameters"
58+
assertThat(callIntentDataParser.parse(httpBaseUrl)).isNull()
59+
assertThat(callIntentDataParser.parse(httpCallUrl)).isNull()
60+
}
61+
62+
@Test
63+
fun `Element Call urls will be returned as is`() {
5864
val httpsBaseUrl = "https://call.element.io"
59-
val httpsCallUrl = "https://call.element.io/some-actual-call?with=parameters"
60-
assertThat(callIntentDataParser.parse(httpBaseUrl)).isEqualTo(httpBaseUrl)
61-
assertThat(callIntentDataParser.parse(httpCallUrl)).isEqualTo(httpCallUrl)
62-
assertThat(callIntentDataParser.parse(httpsBaseUrl)).isEqualTo(httpsBaseUrl)
63-
assertThat(callIntentDataParser.parse(httpsCallUrl)).isEqualTo(httpsCallUrl)
65+
val httpsCallUrl = VALID_CALL_URL_WITH_PARAM
66+
assertThat(callIntentDataParser.parse(httpsBaseUrl)).isEqualTo("$httpsBaseUrl?$EXTRA_PARAMS")
67+
assertThat(callIntentDataParser.parse(httpsCallUrl)).isEqualTo("$httpsCallUrl&$EXTRA_PARAMS")
6468
}
6569

6670
@Test
@@ -76,19 +80,35 @@ class CallIntentDataParserTests {
7680
}
7781

7882
@Test
79-
fun `element scheme with call host and url param gets url extracted`() {
83+
fun `element scheme with call host and url with http will returns null`() {
8084
val embeddedUrl = "http://call.element.io/some-actual-call?with=parameters"
8185
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
8286
val url = "element://call?url=$encodedUrl"
83-
assertThat(callIntentDataParser.parse(url)).isEqualTo(embeddedUrl)
87+
assertThat(callIntentDataParser.parse(url)).isNull()
8488
}
8589

8690
@Test
87-
fun `element scheme 2 with url param gets url extracted`() {
91+
fun `element scheme with call host and url param gets url extracted`() {
92+
val embeddedUrl = VALID_CALL_URL_WITH_PARAM
93+
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
94+
val url = "element://call?url=$encodedUrl"
95+
assertThat(callIntentDataParser.parse(url)).isEqualTo("$VALID_CALL_URL_WITH_PARAM&$EXTRA_PARAMS")
96+
}
97+
98+
@Test
99+
fun `element scheme 2 with url param with http returns null`() {
88100
val embeddedUrl = "http://call.element.io/some-actual-call?with=parameters"
89101
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
90102
val url = "io.element.call:/?url=$encodedUrl"
91-
assertThat(callIntentDataParser.parse(url)).isEqualTo(embeddedUrl)
103+
assertThat(callIntentDataParser.parse(url)).isNull()
104+
}
105+
106+
@Test
107+
fun `element scheme 2 with url param gets url extracted`() {
108+
val embeddedUrl = VALID_CALL_URL_WITH_PARAM
109+
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
110+
val url = "io.element.call:/?url=$encodedUrl"
111+
assertThat(callIntentDataParser.parse(url)).isEqualTo("$VALID_CALL_URL_WITH_PARAM&$EXTRA_PARAMS")
92112
}
93113

94114
@Test
@@ -101,15 +121,15 @@ class CallIntentDataParserTests {
101121

102122
@Test
103123
fun `element scheme 2 with no url returns null`() {
104-
val embeddedUrl = "http://call.element.io/some-actual-call?with=parameters"
124+
val embeddedUrl = VALID_CALL_URL_WITH_PARAM
105125
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
106126
val url = "io.element.call:/?no_url=$encodedUrl"
107127
assertThat(callIntentDataParser.parse(url)).isNull()
108128
}
109129

110130
@Test
111131
fun `element scheme with no call host returns null`() {
112-
val embeddedUrl = "http://call.element.io/some-actual-call?with=parameters"
132+
val embeddedUrl = VALID_CALL_URL_WITH_PARAM
113133
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
114134
val url = "element://no-call?url=$encodedUrl"
115135
assertThat(callIntentDataParser.parse(url)).isNull()
@@ -129,9 +149,39 @@ class CallIntentDataParserTests {
129149

130150
@Test
131151
fun `element invalid scheme returns null`() {
132-
val embeddedUrl = "http://call.element.io/some-actual-call?with=parameters"
152+
val embeddedUrl = VALID_CALL_URL_WITH_PARAM
133153
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
134154
val url = "bad.scheme:/?url=$encodedUrl"
135155
assertThat(callIntentDataParser.parse(url)).isNull()
136156
}
157+
158+
@Test
159+
fun `element scheme 2 with url extra param appPrompt gets url extracted`() {
160+
val embeddedUrl = "${VALID_CALL_URL_WITH_PARAM}&appPrompt=true"
161+
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
162+
val url = "io.element.call:/?url=$encodedUrl"
163+
assertThat(callIntentDataParser.parse(url)).isEqualTo("$VALID_CALL_URL_WITH_PARAM&$EXTRA_PARAMS")
164+
}
165+
166+
@Test
167+
fun `element scheme 2 with url extra param confineToRoom gets url extracted`() {
168+
val embeddedUrl = "${VALID_CALL_URL_WITH_PARAM}&confineToRoom=false"
169+
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
170+
val url = "io.element.call:/?url=$encodedUrl"
171+
assertThat(callIntentDataParser.parse(url)).isEqualTo("$VALID_CALL_URL_WITH_PARAM&$EXTRA_PARAMS")
172+
}
173+
174+
@Test
175+
fun `element scheme 2 with url fragment gets url extracted`() {
176+
val embeddedUrl = "${VALID_CALL_URL_WITH_PARAM}#fragment"
177+
val encodedUrl = URLEncoder.encode(embeddedUrl, "utf-8")
178+
val url = "io.element.call:/?url=$encodedUrl"
179+
assertThat(callIntentDataParser.parse(url)).isEqualTo("$VALID_CALL_URL_WITH_PARAM&$EXTRA_PARAMS#fragment")
180+
}
181+
182+
183+
companion object {
184+
const val VALID_CALL_URL_WITH_PARAM = "https://call.element.io/some-actual-call?with=parameters"
185+
const val EXTRA_PARAMS = "appPrompt=false&confineToRoom=true"
186+
}
137187
}

0 commit comments

Comments
 (0)