Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WebView] Add additionalHttpHeaders to WebContent.Url #1089

Merged
merged 11 commits into from
Apr 6, 2022
9 changes: 6 additions & 3 deletions web/api/current.api
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,13 @@ package com.google.accompanist.web {
}

public static final class WebContent.Url extends com.google.accompanist.web.WebContent {
ctor public WebContent.Url(String url);
ctor public WebContent.Url(String url, optional java.util.Map<java.lang.String,java.lang.String> additionalHttpHeaders);
method public String component1();
method public com.google.accompanist.web.WebContent.Url copy(String url);
method public java.util.Map<java.lang.String,java.lang.String> component2();
method public com.google.accompanist.web.WebContent.Url copy(String url, java.util.Map<java.lang.String,java.lang.String> additionalHttpHeaders);
method public java.util.Map<java.lang.String,java.lang.String> getAdditionalHttpHeaders();
method public String getUrl();
property public final java.util.Map<java.lang.String,java.lang.String> additionalHttpHeaders;
property public final String url;
}

Expand All @@ -70,7 +73,7 @@ package com.google.accompanist.web {
public final class WebViewKt {
method @androidx.compose.runtime.Composable public static void WebView(com.google.accompanist.web.WebViewState state, optional androidx.compose.ui.Modifier modifier, optional boolean captureBackPresses, optional com.google.accompanist.web.WebViewNavigator navigator, optional kotlin.jvm.functions.Function1<? super android.webkit.WebView,kotlin.Unit> onCreated, optional com.google.accompanist.web.AccompanistWebViewClient client, optional com.google.accompanist.web.AccompanistWebChromeClient chromeClient);
method @androidx.compose.runtime.Composable public static com.google.accompanist.web.WebViewNavigator rememberWebViewNavigator(optional kotlinx.coroutines.CoroutineScope coroutineScope);
method @androidx.compose.runtime.Composable public static com.google.accompanist.web.WebViewState rememberWebViewState(String url);
method @androidx.compose.runtime.Composable public static com.google.accompanist.web.WebViewState rememberWebViewState(String url, optional java.util.Map<java.lang.String,java.lang.String> additionalHttpHeaders);
method @androidx.compose.runtime.Composable public static com.google.accompanist.web.WebViewState rememberWebViewStateWithHTMLData(String data, optional String? baseUrl);
}

Expand Down
31 changes: 31 additions & 0 deletions web/src/androidTest/kotlin/com/google/accompanist/web/WebTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,37 @@ class WebTest {
assertThat(navigator.canGoForward).isTrue()
}

@Test
fun testAdditionalHttpHeaders() {
val mockServer = MockWebServer()
mockServer.start()
val baseUrl = mockServer.url("/")

rule.setContent {
val state = rememberWebViewState(
url = baseUrl.toString(),
additionalHttpHeaders = mapOf(
"first-additional-header" to "first",
"second-additional-header" to "second",
)
)

WebTestContent(
webViewState = state,
idlingResource = idleResource,
)
}

rule.waitForIdle()

val request = mockServer.takeRequest()

assertThat(request.getHeader("first-additional-header")).isEqualTo("first")
assertThat(request.getHeader("second-additional-header")).isEqualTo("second")

mockServer.shutdown()
}

private val webNode: SemanticsNodeInteraction
get() = rule.onNodeWithTag(WebViewTag)
}
Expand Down
32 changes: 24 additions & 8 deletions web/src/main/java/com/google/accompanist/web/WebView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ import androidx.compose.ui.viewinterop.AndroidView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

Expand Down Expand Up @@ -109,7 +108,7 @@ fun WebView(
val url = content.url

if (url.isNotEmpty() && url != view.url) {
view.loadUrl(url)
view.loadUrl(url, content.additionalHttpHeaders.toMutableMap())
}
}
is WebContent.Data -> {
Expand Down Expand Up @@ -167,7 +166,7 @@ open class AccompanistWebViewClient : WebViewClient() {
!url.startsWith("data:text/html") &&
state.content.getCurrentUrl() != url
) {
state.content = WebContent.Url(url)
state.content = state.content.withUrl(url)
}
}

Expand All @@ -190,8 +189,7 @@ open class AccompanistWebViewClient : WebViewClient() {
// Override all url loads to make the single source of truth
// of the URL the state holder Url
request?.let {
val content = WebContent.Url(it.url.toString())
state.content = content
state.content = state.content.withUrl(it.url.toString())
}
return true
}
Expand Down Expand Up @@ -227,7 +225,11 @@ open class AccompanistWebChromeClient : WebChromeClient() {
}

sealed class WebContent {
data class Url(val url: String) : WebContent()
data class Url(
val url: String,
val additionalHttpHeaders: Map<String, String> = emptyMap(),
) : WebContent()

data class Data(val data: String, val baseUrl: String? = null) : WebContent()

fun getCurrentUrl(): String? {
Expand All @@ -238,6 +240,11 @@ sealed class WebContent {
}
}

internal fun WebContent.withUrl(url: String) = when (this) {
is WebContent.Url -> copy(url = url)
else -> WebContent.Url(url)
}

/**
* Sealed class for constraining possible loading states.
* See [Loading] and [Finished].
Expand Down Expand Up @@ -394,10 +401,19 @@ data class WebViewError(
* Creates a WebView state that is remembered across Compositions.
*
* @param url The url to load in the WebView
* @param additionalHttpHeaders Optional, additional HTTP headers that are passed to [WebView.loadUrl].
* Note that these headers are used for all subsequent requests of the WebView.
*/
@Composable
fun rememberWebViewState(url: String) =
remember(url) { WebViewState(WebContent.Url(url)) }
fun rememberWebViewState(url: String, additionalHttpHeaders: Map<String, String> = emptyMap()) =
remember(url, additionalHttpHeaders) {
WebViewState(
WebContent.Url(
url = url,
additionalHttpHeaders = additionalHttpHeaders
)
)
}

/**
* Creates a WebView state that is remembered across Compositions.
Expand Down