I was trying to intercept all network communication going in and out of the WebView within the host application. My attempt focused around the two shouldInterceptRequest methods, provided by the WebViewClient:
@Override
public WebResourceResponse shouldInterceptRequest(final WebView view, String url) {
Log.d(Constants.Tags.WEBVIEW_CLIENT, "WV REQUEST (OLD) " + url);
return processRequest(url);
}
@Override
@TargetApi(21)
public WebResourceResponse shouldInterceptRequest(final WebView view, WebResourceRequest
interceptedRequest) {
Log.d(Constants.Tags.WEBVIEW_CLIENT, "WV REQUEST (NEW) " + interceptedRequest.getUrl
().toString() + " " + interceptedRequest.getMethod());
return processRequest(interceptedRequest);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private WebResourceResponse processRequest(WebResourceRequest ir) {
if (!"GET".equals(ir.getMethod())) {
Log.d(Constants.Tags.WEBVIEW_CLIENT, "IGNORING " + ir.getMethod() + " " + ir.getUrl());
return null;
}
return processRequest(ir.getUrl().toString());
}
private WebResourceResponse processRequest(String url) {
Request request = new Request.Builder().url(url).build();
try {
return processResponse(okHttpClient.newCall(request).execute());
} catch (SSLHandshakeException e) {
Log.d(Constants.Tags.WEBVIEW_CLIENT, "SSLHandshakeException: " + e.getMessage());
} catch (IOException e) {
Log.d(Constants.Tags.WEBVIEW_CLIENT, "IOException: " + e.getMessage());
e.printStackTrace();
}
return null;
}
private WebResourceResponse processResponse(Response response) {
String contentType = response.body().contentType().toString();
if (contentType != null) {
String mimeType = contentType;
if (contentType.contains(";")) {
mimeType = contentType.split(";")[0].trim();
}
WebResourceResponse webResourceResponse = new WebResourceResponse(mimeType, response
.header("content-encoding", "utf-8")
, response.body().byteStream());
if (Build.VERSION.SDK_INT > 21) {
webResourceResponse.setResponseHeaders(convertHeaders(response.headers()));
webResourceResponse.setStatusCodeAndReasonPhrase(response.code(),"whatever");
}
return webResourceResponse;
}
return null;
}
private Map<String, String> convertHeaders(Headers headers) {
Map<String, String> map = new HashMap<>();
for (int i = 0; i < headers.size(); i++) {
map.put(headers.name(i), headers.value(i));
}
return map;
}
In order to make this a viable solution, I'm trying to attach all the headers and the response code to the WebResourceResponse object on API level 21 or over. One of the reasons why I believe this is impossible to do, even if I skip anything but GET requests is the fact that doesn't support the interception of redirects. That's valid both ways: Application --> Webview and WebView --> Application.
Excerpt from WebResourceResponse's documentation, regarding the value of the status code parameter in setStatusCodeAndReasonPhrase:
int: the status code needs to be in the ranges [100, 299], [400, 599]. Causing a redirect by specifying a 3xx code is not supported.
I managed to get response code 302 to work on browsers on Lollipop or newer by bypassing the monitored setter with reflection. This is only valid for Application --> WebView, since the WebView won't acknowledge the WebViewClient that it's executing a redirect request (next step after i pass back the hacky 302 WebResourceResponse). shouldInterceptRequest is simply not being called for that request.
I'm not going to bother anyone with my initial intentions. I simply want to know why doesn't the modern WebView support redirects? So far I'm working with the conclusion that the reason is compatibility with pre 4.4 WebView providers (potentially not Chromium based). That, however, doesn't make much sense: I simply can't make a call to setStatusCodeAndReasonPhrase on Android versions below 5.0.
Any feedback is highly appreciated.