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

[Web] Add a method to expose WebViewClient and WebChromeClient #1097

Merged
merged 4 commits into from Mar 29, 2022

Conversation

bentrengrove
Copy link
Collaborator

Fixes #1095
Fixes #1071

Currently Accompanist Web sets it's own clients and doesn't provide a way to customise them. This limits the functionality and has created some obvious missing features. This is an attempt to expose the clients whilst maintaining the existing functionality. I did this via subclassing as I couldn't think of another way to do it, the clients themselves are classes not interfaces which limited the possibilities.

Open to other suggestions.

The other thing is I removed the onError callback as it was now duplicating functionality. I decided not to mark it as deprecated because this library was never released in a non-alpha version but I can create a deprecated version of it if needed.

@bentrengrove bentrengrove changed the title Add a method to expose WebViewClient and WebChromeClient [Web] Add a method to expose WebViewClient and WebChromeClient Mar 22, 2022
Copy link
Collaborator

@manuelvicnt manuelvicnt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some nits! Overall LGTM :) thanks!

@@ -115,13 +119,22 @@ class BasicWebViewSample : ComponentActivity() {
)
}

// A custom WebViewClient and WebChromeClient can be provided via subclassing
val webClient = object : AccompanistWebViewClient() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this webClient should be remembered

* As Accompanist Web needs to set its own web client to function, it provides this intermediary
* class that can be overriden if further custom behaviour is required.
*/
open class AccompanistWebViewClient : WebViewClient() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sad truth: I wish we could make these interfaces 😞

@@ -467,6 +529,8 @@ private fun WebTestContent(
webViewState: WebViewState,
idlingResource: WebViewIdlingResource,
navigator: WebViewNavigator = rememberWebViewNavigator(),
client: AccompanistWebViewClient = AccompanistWebViewClient(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remember the default value

@@ -467,6 +529,8 @@ private fun WebTestContent(
webViewState: WebViewState,
idlingResource: WebViewIdlingResource,
navigator: WebViewNavigator = rememberWebViewNavigator(),
client: AccompanistWebViewClient = AccompanistWebViewClient(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remember this client

@@ -467,6 +529,8 @@ private fun WebTestContent(
webViewState: WebViewState,
idlingResource: WebViewIdlingResource,
navigator: WebViewNavigator = rememberWebViewNavigator(),
client: AccompanistWebViewClient = AccompanistWebViewClient(),
chromeClient: AccompanistWebChromeClient = AccompanistWebChromeClient()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto: remember

@@ -467,6 +529,8 @@ private fun WebTestContent(
webViewState: WebViewState,
idlingResource: WebViewIdlingResource,
navigator: WebViewNavigator = rememberWebViewNavigator(),
client: AccompanistWebViewClient = AccompanistWebViewClient(),
chromeClient: AccompanistWebChromeClient = AccompanistWebChromeClient()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto: remember

@@ -68,7 +68,8 @@ fun WebView(
captureBackPresses: Boolean = true,
navigator: WebViewNavigator = rememberWebViewNavigator(),
onCreated: (WebView) -> Unit = {},
onError: (request: WebResourceRequest?, error: WebResourceError?) -> Unit = { _, _ -> }
client: AccompanistWebViewClient = AccompanistWebViewClient(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto: remember these two

@@ -467,6 +529,8 @@ private fun WebTestContent(
webViewState: WebViewState,
idlingResource: WebViewIdlingResource,
navigator: WebViewNavigator = rememberWebViewNavigator(),
client: AccompanistWebViewClient = AccompanistWebViewClient(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remember this client

* class that can be overriden if further custom behaviour is required.
*/
open class AccompanistWebViewClient : WebViewClient() {
internal lateinit var state: WebViewState
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make these two open so that they can be intercepted by another impl?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't want people to be able to go webViewClient.state = someOtherState when using it so maybe?

open lateinit var state: WebViewState
        internal set

* class that can be overriden if further custom behaviour is required.
*/
open class AccompanistWebChromeClient : WebChromeClient() {
internal lateinit var state: WebViewState
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto: consider open so that it can be intercepted

@sampengilly
Copy link
Contributor

sampengilly commented Mar 29, 2022

I think there's a question that comes up with opening this stuff up. Namely, should the compose state behaviour be guaranteed at all times, with the user only able to "hook-in" on top of that? Or should everything be able to be overridden by the user, including the default compose-like behaviours?

I also struggled a bit thinking about what the best way to expose the additional callbacks would be. The only other solution I had in mind outside of subclassing was to have something like the following:

class WebChromeClientCallbacks(
    val onProgressStarted: (...) -> Unit,
    val onProgressFinished: (...) -> Unit,
    ...
)

@Composable
fun rememberWebChromeClientCallbacks(
    onProgressStarted: (...) -> Unit = {},
    onProgressFinished: (...) -> Unit = {},
    ...
) = remember(...) { WebChromeClientCallbacks(...) }

// Same for WebViewClient

@Composable
fun WebView(
    ...
    webChromeClientCallbacks: WebChromeClientCallbacks = rememberWebChromeClientCallbacks()
) {
   // When setting up the WebChromeClient and WebViewClient, call 
   // the appropriate callback after handling the more compose-like behaviours
}

That would fall into the "compose-like behaviours are guaranteed, user can hook-in for anything else on top of that" approach. Similarly if the subclassing method somehow required a super call.

@bentrengrove
Copy link
Collaborator Author

The subclasses will be required to call super but that applies to when you use webview without Compose as well I think. I did think about doing the callbacks in a class idea as well, that's how Maps Compose did it, it just seemed like there were so many that we may as well match the implementation of webview.

I am happy with this approach for now but we can change it if we get more feature requests/bugs for things people can't do.

@bentrengrove bentrengrove merged commit c3af5e8 into main Mar 29, 2022
@bentrengrove bentrengrove deleted the ben/expose_web_client branch March 29, 2022 21:59
@Doomsdayrs
Copy link

Could this not have been accomplished with a "configure webviewclient" function?

@sampengilly
Copy link
Contributor

Just had a thought, wouldn't this also need to set the clients in the update callback of AndroidView() to cover any changes over time?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants