how to get Raw HTML response of android webViewClient - android

Solution must work on API >20
This solution does not work. I assume it works only on lower versions of the Android API. I'm testing on Android 8.0.
I've tried using Retrofit as soon as the page is loaded, instead of getting the html via WebViewClient. But the user is not logged in in the Retrofit request. And I'm doing the same request 2 times then.
#SuppressLint("JavascriptInterface")
private fun initializeWebView(url : String?) {
binding.webView.loadUrl(url)
binding.webView.settings.javaScriptEnabled = true
binding.webView.settings.useWideViewPort = true
binding.webView.requestFocus(View.FOCUS_DOWN)
binding.webView.addJavascriptInterface(MyJavaScriptInterface(context!!), "HtmlViewer");
binding.webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
return false
}
override fun onPageFinished(view: WebView?, url: String?) {
// super.onPageFinished(view, url)
//tried adding a sleep, but doesn't work:
Thread.sleep(6000)
//showHTML method is not being called:
binding.webView.loadUrl("javascript:window.HtmlViewer.showHTML" +
"('<html>'+document.getElementsByTagName('html')[0].innerHTML+'</html>');")
// Prints out: html: null
binding.webView.evaluateJavascript(
"javascript:window.HtmlViewer.showHTML" +
"('<html>'+document.getElementsByTagName('html')[0].innerHTML+'</html>');"
) { html ->
Log.d("HTML", html)
// code here
}
}
}
}
class MyJavaScriptInterface(val context: Context) {
fun showHTML(html: String) {
Log.d("",""+html)
AlertDialog.Builder(context).setTitle("HTML").setMessage(html)
.setPositiveButton(android.R.string.ok, null).setCancelable(false).create().show() }
}

Related

Use user certificates inside webview fails

I'm trying to embed a webview which embed an url which use a personal certificate to authenticate in the web. If I use a normal Chrome, when I reach this point, a system dialog appears to select the certificate but when I tried on the webview, when I reach this point of the system dialog, the web fails with message that cannot access.
Is it possible to do that? This is my code for the webview.
val myWebView: WebView = findViewById(R.id.wv)
myWebView.loadUrl(url)
myWebView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(
view: WebView?,
request: WebResourceRequest?
): Boolean {
return false
}
}
myWebView.webChromeClient = object : WebChromeClient () {
override fun onJsAlert(
view: WebView?,
url: String?,
message: String?,
result: JsResult?
): Boolean {
return super.onJsAlert(view, url, message, result)
}
override fun onJsConfirm(
view: WebView?,
url: String?,
message: String?,
result: JsResult?
): Boolean {
return super.onJsConfirm(view, url, message, result)
}
override fun onJsPrompt(
view: WebView?,
url: String?,
message: String?,
defaultValue: String?,
result: JsPromptResult?
): Boolean {
return super.onJsPrompt(view, url, message, defaultValue, result)
}
}
myWebView.settings.javaScriptCanOpenWindowsAutomatically = true
myWebView.settings.javaScriptEnabled = true
myWebView.settings.loadsImagesAutomatically = true
myWebView.settings.loadWithOverviewMode = true
myWebView.settings.domStorageEnabled = true
myWebView.settings.builtInZoomControls = true
myWebView.settings.allowContentAccess = true
Thanks
The Webview is likely using the network security policy of your app, while Chrome is using the system's policy. If this is the case, you can embed your custom cert in the app and add a network_security_config.xml that defines it as trusted. See here for details.

return called before onPageFinished

I have a WebView I use for some simple web scraping, which works fine but when I put it in a function and try to return whatever it scraped the return part is called before the WebView even scraped. How can I make it wait before calling return, since a return inside onPageFinished isn't possible?
fun loadText() : String {
myWebView2 = findViewById(R.id.webView2)
var stringToBeReturned = ""
myWebView2.settings.javaScriptEnabled = true
myWebView2.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(myWebView2, url)
myWebView2.evaluateJavascript("(function() { return ('<html>'+document.getElementsByTagName('html')[0].innerHTML+'</html>'); })();") { value ->
stringToBeReturned = value.substringAfter("on \\u003C/span>\\u003Ca href=\\\"").substringBefore('\\')
}
}
}
myWebView2.loadUrl("https://www.examplewebsite.com")
return stringToBeReturned
}
onPageFinished() is a callback, that gets called asynchronously (whenever the page has been loaded) after your function returns.
You should not have this callback in a function. Instead do the configuration in onCreate(), set the Callback and pass the resultString, once its available, wherever you need it:
override fun onCreate(savedInstanceState: Bundle?) {
val webView: WebView = findViewById(R.id.webView2)
webView.settings.javaScriptEnabled = true
webView.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
// this function gets called as soon the page has been loaded
super.onPageFinished(view, url)
webView.evaluateJavascript("(function() { return ('<html>'+document.getElementsByTagName('html')[0].innerHTML+'</html>'); })();") { value ->
val resultString = value.substringAfter("on \\u003C/span>\\u003Ca href=\\\"").substringBefore('\\')
// pass resultString wherever you need it
}
}
}
}

KlarnaHybridSDK - Getting swamped with the message "Notifying Native Hook" [ANDROID]

Problem description
When debugging the web portion of my app either in chrome://inspect/#devices or directly in android studio I can see that I'm getting swamped with the message "Notifying Native Hook".
When using chrome://inspect/#devices to inspect the web page used in the app I see that the message has the source VM298:5 (quite sure that I've seen it with another number as well after VM). When clicking on the source I find the following function:
(function () {
if (document.readyState === "complete") {
var foundMessageBridge = false
function notifyMessageBridge() {
console.log("Notifying Native Hook")
if (window.__KlarnaNativeHook != null) {
console.log("Klarna Native Hook was notified")
window.__KlarnaNativeHook.setNativeReady()
foundMessageBridge = true
} else {
window.setTimeout(notifyMessageBridge, 500)
}
}
notifyMessageBridge()
}
window.addEventListener('load', () => {
let interval = null
let notifyMessageBridge = () => {
if (window.__KlarnaNativeHook != null) {
window.__KlarnaNativeHook.setNativeReady()
clearInterval(interval)
}
}
interval = setInterval(notifyMessageBridge, 500)
})
}())
Which finally led me to that it has something to do with Klarna. Since I'm getting swamped with this I suppose that my app is not getting into the if (window.__KlarnaNativeHook != null).
After some debugging I noticed the following:
When I start the interaction with Klarna my app enters if (window.__KlarnaNativeHook != null). But only once and the spamming resumes afterwards.
If I remove the line (activity as MainActivity).klarnaHybridSDK.addWebView(myWebView) the message swamping stops. Which I suppose is not that surprising.
Apart from my app's web page empty untitled web pages are generated which only prints out "Notifying Native Hook" in the console and nothing else, no code, nothing.
Apart from the swamping the app is working fine with Klarna.
I'm not sure if this is a bug in the SDK or an error of mine.
Any suggestions on how to stop the spamming "Notifying Native Hook"?
(I know how to filter it, but 120 messages/min * (my app's webpage + the untitleds generated) and doing it every time is getting tiresome)
Code
MainActivity
class MainActivity : AppCompatActivity(), KlarnaHybridSDKCallback {
...
lateinit var klarnaHybridSDK : KlarnaHybridSDK
override fun onCreate(savedInstanceState: Bundle?) {
klarnaHybridSDK = KlarnaHybridSDK("myapp://", this)
...
}
override fun didHideFullscreenContent(webView: WebView, completion: OnCompletion) {
Timber.i("Klarna didHideFullscreenContent called")
completion.run()
}
override fun didShowFullscreenContent(webView: WebView, completion: OnCompletion) {
Timber.i("Klarna didShowFullscreenContent called")
completion.run()
}
override fun onErrorOccurred(webView: WebView, error: KlarnaMobileSDKError) {
Timber.i("Klarna onErrorOccurred called")
Timber.i("Klarna Error: $error")
}
override fun willHideFullscreenContent(webView: WebView, completion: OnCompletion) {
Timber.i("Klarna willHideFullscreenContent called")
completion.run()
}
override fun willShowFullscreenContent(webView: WebView, completion: OnCompletion) {
Timber.i("Klarna willShowFullscreenContent called")
completion.run()
}
}
Fragment Class
class myFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
...
/*WEBVIEW*/
myWebView.settings.javaScriptEnabled = true
WebView.setWebContentsDebuggingEnabled(true)
val context = this.activity?.applicationContext
myWebView.addJavascriptInterface(
WebAppInterface(
myWebView, context
), "Android")
myWebView.settings.loadWithOverviewMode = true
myWebView.settings.useWideViewPort = true
myWebView.settings.domStorageEnabled = true
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP) {
myWebView.settings.mixedContentMode = WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE
}
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT){
myWebView.settings.cacheMode = WebSettings.LOAD_NO_CACHE
if(this.activity?.applicationInfo?.flags != 0 && ApplicationInfo.FLAG_DEBUGGABLE != 0) {
WebView.setWebContentsDebuggingEnabled(true)
Timber.i("setWebContentsDebuggingEnabled")
}
}
ticketWebView.webViewClient = object: WebViewClient(){
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
...
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
...
if (view != null) {
(activity as MainActivity).klarnaHybridSDK.newPageLoad(view)
}
...
}
override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) {
...
}
override fun onReceivedHttpError(view: WebView?, request: WebResourceRequest?, errorResponse: WebResourceResponse?) {
super.onReceivedHttpError(view, request, errorResponse)
...
//Added to prevent Klarna's address search failing to cause the app to show the error screen.
if (request?.url.toString().contains("klarna.com/eu/address")) {
Timber.i("${receivedError}")
Timber.i("Ignoring error since it's simply Klarna's address search failing")
}
...
}
#TargetApi(21)
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
val url = request?.url
...
return (activity as MainActivity).klarnaHybridSDK.shouldFollowNavigation(url.toString())
}
/*To handle cases where the target API < 21*/
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
return (activity as MainActivity).klarnaHybridSDK.shouldFollowNavigation(url.toString())
}
}
val url = "myAppUrl.php"
val postData = "data=" + URLEncoder.encode(data, "UTF-8").toString()
myWebView.postUrl(url, postData.toByteArray())
return root
}
...
}
Steps to reproduce
Started the app
Opened chrome://inspect/#devices in Chrome.
Located the relevant web page
Pressed "Inspect"
Expected behavior
Not getting swamped with the message in my console.
Device and version:
Device: Samsung A10
OS version: Android 9
Klarna In-App SDK version: com.klarna.mobile:sdk:2.0.16
Firstly, thanks for reporting this issue. Fortunately, this problem will be fixed with a future release.
To give you more insight, this currently is an issue when the SDK tries to notify Klarna components in a WebView with no content; thus resulting in a page called ‘untitled’ with excessive number of logs.
Temporarily, this can be avoided by adding the WebView to the Hybrid SDK when a page is loaded.
Edit: #Myalakin We would like to inform you that this issue has been fixed by the latest releases of our SDK.

Android WebView not showing content at Api 20

I have a WebView in my app. It works properly but at Api 20, WebView does not show the content.
I checked the logs and see these errors
I/chromium: [INFO:CONSOLE(7)] "The key "shrink-to-fit" is not recognized and ignored.", source: https://...
I/chromium: [INFO:CONSOLE(43)] "Uncaught SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode", source: https://... (43)
I overviewed some answers and they generally suggest to use
webView.settings.useWideViewPort = true
webView.settings.loadWithOverviewMode = true
I have already implement these settings but still same error happens. WebView shows noting
Here is my WebView Settings
/**
* Initialize the WebView
*/
#SuppressLint("SetJavaScriptEnabled")
private fun initWebView() {
webView.webViewClient = object : WebViewClient() {
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
return super.shouldOverrideUrlLoading(view, request)
}
override fun onPageStarted(
view: WebView, url: String, favicon: Bitmap?) {
showProgressDialog()
}
override fun onPageFinished(view: WebView, url: String) {
hideProgressDialog()
}
#RequiresApi(api = Build.VERSION_CODES.M)
override fun onReceivedError(view: WebView, request: WebResourceRequest, error: WebResourceError) {
// Your code to do
hideProgressDialog()
}
override fun onReceivedError(view: WebView, errorCode: Int, description: String, failingUrl: String) {
// Handle the error
hideProgressDialog()
}
override fun onReceivedSslError(view: WebView, handler: SslErrorHandler?, error: SslError) {
// ignore ssl error
if (handler != null) {
handler.proceed()
} else {
super.onReceivedSslError(view, null, error)
}
}
}
webView.isHorizontalScrollBarEnabled = false
webView.settings.javaScriptEnabled = true
webView.settings.useWideViewPort = true
webView.setInitialScale(1)
webView.settings.loadWithOverviewMode = true
context?.let { webView.setBackgroundColor(ContextCompat.getColor(it, R.color.colorDarkBlue)) }
}
/**
* Load url
*/
private fun loadPage(url: String) {
webView.loadUrl(url)
}
Someone have a idea? I will be appreciate for any help

JavaScriptInterface in a WebView is not called on Lollipop and Marshmallow

I have tried it on android 7.0, it works, but on android 5,6 it does not work, i could not find any clue on what maybe going wrong.
The URL redirect to payment portal and at the end it calls Android.postMessage("success") from Javascript, but it does not get called in native this problem only persist in android 5 and maybe on android 6 too.
My minimum SDK level is 21.
class PaymentActivity : BaseActivity<ActivityPaymentBinding, IPaymentMvvm.ViewModel>(), IPaymentMvvm.View {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityComponent.inject(this)
setAndBindContentView(savedInstanceState, R.layout.activity_payment)
viewModel.initVM()
binding.web.webViewClient = Web(viewModel)
binding.web.settings.javaScriptEnabled = true
binding.web.settings.loadWithOverviewMode = true
binding.web.settings.useWideViewPort = true
binding.web.addJavascriptInterface(JSBridge(this, this), "Android")
val url = intent.extras.getString("url")
if (url == null)
finish()
binding.web.loadUrl(url)
}
override fun onOptionsItemSelected(menuItem: MenuItem): Boolean {
when (menuItem.itemId) {
android.R.id.home ->
finish()
}
return super.onOptionsItemSelected(menuItem)
}
class Web(val viewModel: IPaymentMvvm.ViewModel) : WebViewClient() {
init {
viewModel.setProgress(true)
}
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
view?.loadUrl(request?.url.toString())
return true
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
viewModel.setProgress(false)
}
override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) {
super.onReceivedError(view, request, error)
viewModel.setProgress(false)
Timber.e(error.toString())
}
}
}
class JSBridge(val context: Context, val activity: Activity) {
#JavascriptInterface
fun postMessage(message: String) {
// here we return true if we handled the post.
Timber.i(message)
context.toast(message)
val intent = Intent(context, OrderThanksActivity::class.java)
intent.putExtra(Enums.contactDetail.contactNumber.name, activity.intent.getStringExtra(Enums.contactDetail.contactNumber.name))
intent.putExtra(Enums.contactDetail.phoneNumber.name, activity.intent.getStringExtra(Enums.contactDetail.phoneNumber.name))
intent.putExtra(Enums.contactDetail.cellNumber.name, activity.intent.getStringExtra(Enums.contactDetail.cellNumber.name))
activity.startActivity(intent)
activity.finish()
}
}
It was the problem of JavaScript script from web, which threw an error in a script running in web view before my interface function even get called, but the weird behavior was that script only got terminated on Android 5,6 while Android 7 web view does not terminate execution and execute the whole script regarding error that's why the interface function got called in Android 7.

Categories

Resources