Created
February 25, 2021 07:37
-
-
Save alexcohn/c1f756e8dd95f7eb1251c8bd9ccd8ea8 to your computer and use it in GitHub Desktop.
WebView.postUrl with additionalHttpHeaders via extension
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fun WebView.postUrl(url: String, postData: ByteArray, additionalHttpHeaders: MutableMap<String, String>) { | |
val savedWebViewClient = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { | |
webViewClient | |
} | |
else { | |
getLegacyWebViewClient() | |
} | |
webViewClient = object : WebViewClient() { | |
@Suppress("DEPRECATION") | |
override fun shouldInterceptRequest(view: WebView, requestUrl: String): WebResourceResponse? { | |
if (requestUrl != url) { | |
if (savedWebViewClient != null) { | |
webViewClient = savedWebViewClient | |
} | |
return savedWebViewClient?.shouldInterceptRequest(view, requestUrl) | |
} | |
Log.d("WebViewClient", "(url) post \"${postData.decodeToString()}\" to ${requestUrl}") | |
val httpsUrl = URL(requestUrl) | |
val conn: HttpsURLConnection = httpsUrl.openConnection() as HttpsURLConnection | |
conn.requestMethod = "POST" | |
for ((key, value) in additionalHttpHeaders) { | |
conn.addRequestProperty(key, value) | |
} | |
val os = conn.outputStream | |
os.write(postData) | |
os.close() | |
val responseCode = conn.responseCode | |
Log.d("WebViewClient", "responseCode = ${responseCode} ${conn.contentType}") | |
try { | |
val response = conn.inputStream.readBytes().decodeToString() | |
if (savedWebViewClient != null) { | |
webViewClient = savedWebViewClient | |
} | |
view.post { | |
view.loadData(response, conn.contentType, conn.contentEncoding) | |
} | |
} | |
catch (ex: Exception) { | |
return null | |
} | |
return WebResourceResponse(null, null, null) | |
} | |
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) | |
override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? { | |
val requestUrl = request.url!!.toString() | |
if (!request.isForMainFrame || request.method != "POST" || requestUrl != url) { | |
if (savedWebViewClient != null) { | |
view.post { | |
webViewClient = savedWebViewClient | |
} | |
} | |
return savedWebViewClient?.shouldInterceptRequest(view, request) | |
} | |
Log.d("WebViewClient", "(request) ${request.method} \"${postData.decodeToString()}\" to ${requestUrl}") | |
val httpsUrl = URL(requestUrl) | |
val conn: HttpsURLConnection = httpsUrl.openConnection() as HttpsURLConnection | |
conn.requestMethod = "POST" | |
for ((key, value) in request.requestHeaders) { | |
conn.addRequestProperty(key, value) | |
} | |
for ((key, value) in additionalHttpHeaders) { | |
conn.addRequestProperty(key, value) | |
} | |
val os = conn.outputStream | |
os.write(postData) | |
os.close() | |
val responseCode = conn.responseCode | |
Log.d("WebViewClient", "responseCode=${responseCode} ${conn.contentType}") | |
view.post { | |
if (savedWebViewClient != null) { | |
webViewClient = savedWebViewClient | |
} | |
} | |
val responseHeaders = mutableMapOf<String, String>() | |
for ((key, list) in conn.headerFields) { | |
if (key != null && list != null && list.size >= 1) { | |
responseHeaders[key] = list.get(0) | |
} | |
} | |
// typical conn.contentType is "text/html; charset=UTF-8"; conn.contentEncoding is less reliable | |
return WebResourceResponse( | |
conn.contentType.substringBefore(";"), | |
conn.contentType.substringAfter("charset=", "UTF-8"), | |
conn.responseCode, | |
conn.responseMessage, | |
responseHeaders, | |
conn.inputStream) | |
} | |
@Suppress("DEPRECATION") | |
override fun shouldOverrideUrlLoading(view: WebView, url: String?): Boolean { | |
if (savedWebViewClient == null) { | |
return false | |
} | |
return savedWebViewClient.shouldOverrideUrlLoading(view, url) | |
} | |
@RequiresApi(Build.VERSION_CODES.N) | |
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean { | |
if (savedWebViewClient == null) { | |
return false | |
} | |
return savedWebViewClient.shouldOverrideUrlLoading(view, request) | |
} | |
override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) { | |
if (savedWebViewClient != null) { | |
view.post { | |
webViewClient = savedWebViewClient | |
} | |
savedWebViewClient.onPageStarted(view, url, favicon) | |
} | |
} | |
override fun onPageFinished(view: WebView, url: String?) { | |
Log.w("WebViewClient", "unexpected: onPageFinished ${url}") | |
} | |
override fun onLoadResource(view: WebView, url: String?) { | |
Log.w("WebViewClient", "unexpected: onLoadResource ${url}") | |
} | |
override fun onPageCommitVisible(view: WebView, url: String?) { | |
Log.w("WebViewClient", "unexpected: onPageCommitVisible ${url}") | |
} | |
override fun onTooManyRedirects(view: WebView, cancelMsg: Message, continueMsg: Message?) { | |
Log.w("WebViewClient", "unexpected: onTooManyRedirects ${url}") | |
if (savedWebViewClient != null) { | |
view.post { | |
webViewClient = savedWebViewClient | |
} | |
savedWebViewClient.onPageStarted(view, url, favicon) | |
} | |
else { | |
super.onTooManyRedirects(view, cancelMsg, continueMsg) | |
} | |
} | |
@Suppress("DEPRECATION") | |
override fun onReceivedError(view: WebView, errorCode: Int, description: String?, failingUrl: String?) { | |
Log.i("WebViewClient", "onReceivedError ${url}") | |
if (savedWebViewClient != null) { | |
view.post { | |
webViewClient = savedWebViewClient | |
} | |
savedWebViewClient.onReceivedError(view, errorCode, description, failingUrl) | |
} | |
else { | |
super.onReceivedError(view, errorCode, description, failingUrl) | |
} | |
} | |
@RequiresApi(Build.VERSION_CODES.M) | |
override fun onReceivedError(view: WebView, request: WebResourceRequest, error: WebResourceError) { | |
Log.i("WebViewClient", "onReceivedError ${request.url}") | |
if (savedWebViewClient != null) { | |
view.post { | |
webViewClient = savedWebViewClient | |
} | |
savedWebViewClient.onReceivedError(view, request, error) | |
} | |
else { | |
super.onReceivedError(view, request, error) | |
} | |
} | |
@RequiresApi(Build.VERSION_CODES.M) | |
override fun onReceivedHttpError(view: WebView, request: WebResourceRequest, errorResponse: WebResourceResponse?) { | |
Log.i("WebViewClient", "onReceivedHttpError ${request.url}") | |
if (savedWebViewClient != null) { | |
view.post { | |
webViewClient = savedWebViewClient | |
} | |
savedWebViewClient.onReceivedHttpError(view, request, errorResponse) | |
} | |
else { | |
super.onReceivedHttpError(view, request, errorResponse) | |
} | |
} | |
override fun onFormResubmission(view: WebView, dontResend: Message, resend: Message?) { | |
Log.i("WebViewClient", "onFormResubmission") | |
if (savedWebViewClient != null) { | |
view.post { | |
webViewClient = savedWebViewClient | |
} | |
savedWebViewClient.onFormResubmission(view, dontResend, resend) | |
} | |
else { | |
super.onFormResubmission(view, dontResend, resend) | |
} | |
} | |
override fun doUpdateVisitedHistory(view: WebView, url: String?, isReload: Boolean) { | |
Log.i("WebViewClient", "doUpdateVisitedHistory") | |
if (savedWebViewClient != null) { | |
view.post { | |
webViewClient = savedWebViewClient | |
} | |
savedWebViewClient.doUpdateVisitedHistory(view, url, isReload) | |
} | |
else { | |
super.doUpdateVisitedHistory(view, url, isReload) | |
} | |
} | |
} | |
postUrl(url, postData) | |
} | |
fun WebView.getLegacyWebViewClient(): WebViewClient? { | |
try { | |
val mProviderField = javaClass.getDeclaredField("mProvider") | |
mProviderField.isAccessible = true | |
val mProvider = mProviderField.get(this) | |
if (mProvider != null) { | |
val mContentsClientAdapterField = mProvider.javaClass.getDeclaredField("mContentsClientAdapter") | |
mContentsClientAdapterField.isAccessible = true | |
val mContentsClientAdapter = mContentsClientAdapterField.get(mProvider) | |
if (mContentsClientAdapter != null) { | |
val mWebViewClientField = mContentsClientAdapter.javaClass.getDeclaredField("mWebViewClient") | |
mWebViewClientField.isAccessible = true | |
val mWebViewClient = mWebViewClientField.get(mContentsClientAdapter) | |
Log.i("WebViewClient", "getLegacyWebViewClient ${mWebViewClient}") | |
return mWebViewClient as WebViewClient | |
} | |
} | |
Log.i("WebViewClient", "getLegacyWebViewClient null") | |
} | |
catch (ex: Throwable) { | |
Log.w("WebViewClient", "getLegacyWebViewClient failed", ex) | |
} | |
return null | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment