WebViewClient onPageFinished never called - javascript

I've looked at several similar posts on S/O but I can't find an answer to my problem. I'm trying to grab the html from a web view but I can't get my "onPageFinished() callback to fire. I've even tried using the progress callback on the WebChromeClient but nothing is working. neigh the web client or web chrome client receive any callbacks. I am running this code as an Android unit test and using a test HTML from the assets directory which I've confirmed via assert and debug is being located. What am I missing?
** Update **
I've stepped through the test method in debug. It does load the html (it's a unit test so I cannot actually see the rendered content). It then blocks on the wait() call, times out then performs the assert at the bottom without ever receiving a callback from either client. (Both the web client and chrome client should/would call notify())
The difference here would be that I am running within an Android unit test. The WebView is NOT attached to any Activity or container. I'm wondering if that has anything to do with the missing callbacks. I'll try sticking it on an Activity to see if it makes any difference.
** /Update **
public class MyWebViewTest extends InstrumentationTestCase {
private WebView webView;
private String html;
public void setUp() throws Exception {
super.setUp();
Context context = getInstrumentation().getContext();
this.webView = new WebView(context);
assertTrue("requires test.html",
Arrays.asList(context.getResources().getAssets().list("www")).contains("test.html"));
}
public void tearDown() throws Exception {
}
#JavascriptInterface
public void viewContent(String content) {
this.html = content;
}
public void testCanLoadAndReadWebContentFromWebView() throws Exception {
webView.addJavascriptInterface(this, "_webContainer");
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView webView, String url) {
super.onPageFinished(webView, url);
onPageLoaded(webView);
}
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
});
webView.setWebChromeClient(new WebChromeClient() {
#Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
if(newProgress==100)
onPageLoaded(webView);
}
});
webView.loadUrl("file:///android_asset/www/test.html");
synchronized (this) {
wait(5000);
}
assertNotNull(html);
}
private void onPageLoaded(WebView webView) {
webView.loadUrl("javascript:window._webContainer.viewContent(document.documentElement.innerHTML);");
synchronized (this) {
notify();
}
}
}

I was correct in my assumption! When the WebView is not attached to an Activity it will not load, render, or do much of anything. I ended up extending ActivityInstrumentationTestCase2 and attaching the web view to an Activity. At that point my breakpoints in the callbacks started lighting up. Here's my working test case:
public class MyWebViewTest extends ActivityInstrumentationTestCase2<TestMeActivity> {
private WebView webView;
private String html;
public MyWebViewTest() {
super(TestMeActivity.class);
}
public void setUp() throws Exception {
super.setUp();
Context context = getInstrumentation().getContext();
this.webView = getActivity().getView();
assertTrue("requires test.html",
Arrays.asList(context.getResources().getAssets().list("www")).contains("test.html"));
}
public void tearDown() throws Exception {
}
#JavascriptInterface
public void viewContent(String content) {
this.html = content;
}
public void testCanLoadAndReadWebContentFromWebView() throws Exception {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
configureWebView();
}
});
synchronized (MyWebViewTest.this) {
MyWebViewTest.this.wait(25000);
}
assertEquals("<head></head><body>\n" +
"Hello\n" +
"\n" +
"</body>",html);
}
private void configureWebView() {
webView.addJavascriptInterface(this, "_webContainer");
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView webView, String url) {
super.onPageFinished(webView, url);
onPageLoaded(webView);
}
});
webView.loadUrl("file:///android_asset/www/test.html");
}
#TargetApi(Build.VERSION_CODES.KITKAT)
private void onPageLoaded(WebView webView) {
webView.evaluateJavascript("javascript:window._webContainer.viewContent(document.documentElement.innerHTML);",
new ValueCallback<String>() {
#Override
public void onReceiveValue(String value) {
synchronized (MyWebViewTest.this) {
MyWebViewTest.this.notify();
}
}
}
);
}
}
and here's the Activity I threw together to back it:
public class TestMeActivity extends Activity{
private WebView view;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
view = new WebView(this);
setContentView(view);
}
public WebView getView() {
return view;
}
}
In short there is quite a bit of work involved in testing the contents of a WebView from an Android TestCase. You have to add a Javascript interface to the web view, insert some Javascript to invoke the internal Javascript interface object, passing the contents of the web view, through the interface and finally collect it in your test case. I sure wish it were easier but this is the best I can come up with.

Related

Android webview not rendering CSS and angular

I am trying to load a URL in to android native webview, but it does not render CSS and Angular
CODE:
WebView wvFAQs = (WebView) mFragmentView.findViewById(R.id.wvUrls);
Map<String, String> headersMap = new HashMap<>();
headersMap.put(ApiConstants.AUTHORIZATION, BuildConfig.SS_API_AUTH);
wvFAQs.setWebViewClient(new WebViewClient() {
#SuppressWarnings("deprecation")
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
#TargetApi(Build.VERSION_CODES.N)
#Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return false;
}
});
wvFAQs.getSettings().setJavaScriptEnabled(true);
wvFAQs.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
wvFAQs.loadUrl(mUrl,headersMap);
ERRORS:
"Uncaught ReferenceError: _ is not defined",
"Uncaught ReferenceError: angular is not defined",
"Uncaught ReferenceError: $ is not defined",
but this URL works fine in mobile browsers (chrome & default android browser).
when i googled this issue, i came to know that i need to enable html5 support feature in webiew, here is the link, but still not working.
Can anyone tell me what might be the issue? is there any issue with the URL or webview?
I use this Java code to load the webview:
public class MainActivity extends AppCompatActivity {
private WebView view;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
view = (WebView) this.findViewById(R.id.webView);
view.getSettings().setJavaScriptEnabled(true);
view.setWebViewClient(new MyBrowser());
view.loadUrl("file:///android_asset/www/index.html"); //try js alert
view.setWebChromeClient(new WebChromeClient()); // adding js alert support
}
private class MyBrowser extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && view.canGoBack()) {
view.goBack(); //method goback()
return true;
}
return super.onKeyDown(keyCode, event);
}
What I can say is if that you use Jquery you need to add it to your assets because external links like from cdn like:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
would not load.

Return the button text from Javascript to Java using WebView

I need to read text on the button which is located on the website. I tried to parse the page using JSOUP, but JSOUP doesn't see Javascript object. That is why I decide to use WebView to read text. But it doesn't work. I need to return the button text from Javascript to Java. Is it possible?
public class MainActivity extends AppCompatActivity {
WebView mWebView;
Button mButton, mClick;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView) findViewById(R.id.web_view);
mButton = (Button) findViewById(R.id.start_URL);
mClick = (Button) findViewById(R.id.click);
}
#Override
protected void onResume() {
super.onResume();
mButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setUseWideViewPort(true);
mWebView.getSettings().setLoadWithOverviewMode(true);
mWebView.getSettings().setDomStorageEnabled(true);
mWebView.setWebViewClient(new PersonalWebView());
mWebView.setWebChromeClient(new PersonalWebChrome());
mWebView.addJavascriptInterface(new JSInterface(), "JSInterface");
mWebView.loadUrl(URL);
mClick.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mWebView.loadUrl("javascript: window.JSInterface.callFromJS('buttonText')");}
}
private class JavaScriptInterface {
#JavascriptInterface
public void callFromJS(String buttonText) {
Log.d("JScript","callFromJS "+ buttonText);
}
}
private class PersonalWebView extends WebViewClient{
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
}
#Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
}
}
private class PersonalWebChrome extends WebChromeClient {
private PersonalWebChrome() {
super();
}
#Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
}
}
#Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
Log.d("TAG", consoleMessage.message()+" "+consoleMessage.sourceId());
return super.onConsoleMessage(consoleMessage);
}
}
I tried to change code, but it doesn't work:
mClick.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mWebView.addJavascriptInterface(new JSInterface(), "JSInterface");
ValueCallback<String> result = new ValueCallback<String>() {
#Override
public void onReceiveValue(String value) {
Log.d("TAG", value);
}
};
mWebView.evaluateJavascript("javascript: window.JSInterface.callFromJS('buttonText')",result);}}
In the Log I see:
D/TAG: Uncaught TypeError: Cannot read property 'callFromJS' of undefined
This is not the correct way of dealing with injected Java objects:
mWebView.addJavascriptInterface(new JavaScriptInterface(), "javaCallback");
mWebView.loadUrl("javascript:window.javaCallback.callFromJS('buttonText')";
Injected objects only become "visible" on the next page load after calling addJavascriptInterface. Loading javascript: URIs doesn't count as a page load.
Check out the sample from the docs:
webView.addJavascriptInterface(new JsObject(), "injectedObject");
webView.loadData("", "text/html", null);
webView.loadUrl("javascript:alert(injectedObject.toString())");
The loadData in between is what actually makes the object to be inserted. You can as well use loadUrl but it needs to be a real page (from the network, assets, about:blank, whatever), but not a javascript: URI.
The problem should be in trying to call callFromJS method before registering interface. You register the JS interface only on the button click:
mClick.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mWebView.addJavascriptInterface(new JSInterface(), "JSInterface");
}
}
But you try to call the callFromJS immediately.
Instead of doing this, move your mWebView.evaluateJavascript("javascript: window.JSInterface.callFromJS('buttonText')",result); inside onClick()

JavascriptInterface Method for WebView not called

I have a very weird problem, my application is an e-reader. to call functions for the web view I use a JavaScript class:
public class MyJavaScriptInterface {
Context mContext;
/* Instantiate the interface and set the context */
MyJavaScriptInterface(Context c) {
mContext = c;
}
#JavascriptInterface
public void functionToCall(String input) {
System.out.println("this function was called: "+ input);
}
}
I have a customized web view where I add my JavaScript class.
public class newBTWebView extends WebView implements {
public newBTWebView(Context context) {
super(context);
init(context);
}
public void init(Context context) {
System.out.println("newBTWebview init");
this.context = context;
this.getSettings().setJavaScriptEnabled(true);
setInitialScale(100);
addJavascriptInterface(new MyJavaScriptInterface(this.context), "HTMLOUT");
}
}
and in my main activity I initiate the web view and call the function using JavaScript:
public class BookReader extends Activity implements WebViewDelegate{
private static Context mContext;
private static newBTWebView testWV;
#Override
protected void onCreate(Bundle savedInstanceState) {
System.out.println("onCreate");
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
testWV = (newBTWebView) findViewById(R.id.mywebview1);
testWV.setDelegate(this);
testWV.setWebChromeClient(new WebChromeClient());
testWV.getSettings().setJavaScriptEnabled(true);
testWV.getSettings().setPluginState(PluginState.ON);
testWV.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url)
{
System.out.println("shouldOverrideUrlLoading: " + url);
}
#Override
public void onPageFinished(WebView view, String url) {
System.out.println("onPageFinished");
}
}
}
public void callMyFunction
{
System.out.println("callMyFunction");
testWV.loadUrl("javascript:window.HTMLOUT.functionToCall('myinput');”);
}
}
The functionToCall was working perfectly, lately in my latest update, as I was testing the application on the device it was still working, but when I submit it on store the functionToCall was not getting called any more. I tested the apk file on the device the functionToCall is not getting called. It only works when I run the app directly from the pc to the device, but using the apk file on the same device as mentioned before the functionToCall doesn't get called.
I have tested on Samsung S4, Samsung Note 10.1, and nexus note. with android 4.4.2, 4.0.4 and 5.0.1.
Have you enabled proguard or some code obfuscation ?if yes you should add keep attributes
-keep public class com.MyJavaScriptInterface
-keep public class * implements com.MyJavaScriptInterface
-keepclassmembers class * implements com.MyJavaScriptInterface {
<fields>;
<methods>;
}
i do not know your classes full qualified names. so please put your fully qualified names.

setJavaScriptEnabled(true) in 'doInBackground' override force closes app

I have followed this very basic example tutorial for loading a URL in a WebView async:
android-asynctask-example.
However the app force closes when i click 'Load Webpage', with the error:
"Can't create handler inside thread that has not called
Looper.prepare()".
When I remove the line "webView.getSettings().setJavaScriptEnabled(true);", the app works fine, and displays a WebView (obviously with no JavaScript enabled in the WebView).
If i leave that line in, but set enabled to false, the app also works without crashing.
I wish to modify this project, however i need JavaScript enabled, so was wondering if anyone had a clue as to why it was failing?
Any help will be greatly appreciated!
Try it this way,
public class MainActivity extends Activity {
final Context context = this;
WebView webView =null;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
private class LoadWebPageASYNC extends AsyncTask<String, Void, String> {
#Override
protected void onPreExecute(String result) {
webView = (WebView) findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
}
#Override
protected String doInBackground(String... urls) {
webView.loadUrl(urls[0]);
return null;
}
#Override
protected void onPostExecute(String result) {
}
}
public void dummyFunc(View view){
Toast.makeText(MainActivity.this, "Button Clicked", Toast.LENGTH_SHORT).show();
}
public void readWebpage(View view) {
LoadWebPageASYNC task = new LoadWebPageASYNC();
task.execute(new String[] { "http://www.javacodegeeks.com" });
}
}

Binding JavaScript code to Android code

Im trying to invoke a method in java from javascript, but this doesn't happen when I run the application in the emulator, the application stops when it is suppose to call the method in java. here is the java code:
import android.os.Bundle;
import android.webkit.WebView;
import com.phonegap.*;
public class App extends DroidGap {
WebView webView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
webView = new WebView(this);
webView.addJavascriptInterface(new message(), "Show");
super.loadUrl("file:///android_asset/www/index.html");
}
class message {
String msg() {
return "Hello World!!";
}
}
}
here is the javascript:
<script type="text/javascript">
{
alert("Start");
alert(Show.msg());
alert("End");
}
</script>
It shows the first alert but nothing thereafter, can anyone help?
Your problem is that you're half using PhoneGap and half not. You're creating a separate WebView class from PhoneGap's. The WebView class that you added "Show" to never gets used. Instead the WebView class that is a member of the super (DroidGap) is.
You should do one of two things.
Use PhoneGap's plugin structure (see examples here)
Don't use PhoneGap at all and have a class that looks more like the following:
public class act extends Activity {
WebView webView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
webView = new WebView(this);
webView.getSettings().setJavaScriptEnabled(true);
// Set JS alert() hook
webView.setWebChromeClient(new WebChromeClient() {
public boolean onJsAlert(WebView view, String url, String message, JsResult result)
{
return false;
}
});
webView.loadUrl("file:///android_asset/www/index.html");
// Add JS libraries
webView.addJavascriptInterface(new message(), "Show");
}
class message {
public String msg() {
return "Hello World!!";
}
}
}
Note that the method msg needs to be public
Why not just use AlertDialog?
private void showDialog(int title, String message) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(title);
builder.setMessage(message);
builder.setPositiveButton(R.string.ok_button, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
}
});
builder.show();
}

Categories

Resources