Call MainActivity method from another class in android - javascript

I'm working on an android app. I want to pass some value from my webview to my android application.
I solved this problem successfully by using the solution given in Passing data from java class to Web View html.
The problem that I'm facing is when I'm trying to call MainActivity methods from JavaScriptInterface.java, the methods are not being called or no errors are given.
here's what I have tried so far:
MainActivity act=new MainActivity();
act.myMethod() //This method is available in my activity
but it is not being called.
and
((MainActivity)getActivity).myMethod();
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.AppTheme);
setContentView(R.layout.activity_main);
}
public void myMethod() {
Toast.makeText(this, "Inside MainActivity", Toast.LENGTH_SHORT).show();
}
}
//JavaScriptInterface class
class JavaScriptInterface {
Context mContext;
JavaScriptInterface(Context c) {
mContext = c;
}
/** Show a toast from the web page */
#JavascriptInterface
public void onButtonClick(String toast1, String toast2) {
Toast.makeText(mContext, toast1+" | "+toast2, Toast.LENGTH_SHORT).show(); //THIS IS WORKING
MainActivity act=new MainActivity();
act.myMethod(); //NOT WORKING
}
}

Technically, the myMethod will get called, but since you create the MainActivity yourself, it's not attached to anything.
In general, you shouldn't create a new MainActivity instance this way. To open a new MainActivity, you use an Intent.
In your case, you should have a reference to the original MainActivity instance, and call this method there. Not create a new one in any way, as you already have it running.
An easy way to solve it:
MainActivity.this.myMethod("Hello there")
You don't have to store mContext . You already are inside of MainActivity.
So, the full code would be:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView myWebView = findViewById(R.id.webview);
myWebView.loadUrl("http://www.google.com");
myWebView.addJavascriptInterface(new WebAppInterface(), "Android");
}
public void myMethod(String test){
Toast.makeText(this, test, Toast.LENGTH_SHORT).show();
}
public class WebAppInterface {
/** Show a toast from the web page */
#JavascriptInterface
public void showToast(String toast) {
MainActivity.this.myMethod("Hello there");
}
}
}
In fact, I think you can even avoid having the MainActivity.this. , and call myMethod directly.

You are creating another object of MainActivity this is why its not getting displayed. You are passing the activity context to the interface so you can just
((MainActivity)mContext).myMethod();
Sample MainActivity
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView myWebView = findViewById(R.id.webview);
myWebView.loadUrl("http://www.google.com");
myWebView.addJavascriptInterface(new WebAppInterface(this), "Android");
}
public void myMethod(String test){
Toast.makeText(this, test, Toast.LENGTH_SHORT).show();
}
public class WebAppInterface {
Context mContext;
/** Instantiate the interface and set the context */
WebAppInterface(Context c) {
mContext = c;
}
/** Show a toast from the web page */
#JavascriptInterface
public void showToast(String toast) {
((MainActivity)mContext).myMethod("hello");
}
}
}
Check out android documentation for more info

Related

Activity crashes on start

I need your help the app crashes when I click the "log in" button, What have I done wrong? (The app should open a new activity with webview inside)
Thanks for answers,
I have 2 activities,
The main code:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
super.onCreate(savedInstanceState);
LoadPage();
}
public Button Btn;
public void LoadPage(){
Btn = (Button)findViewById(R.id.LoginBtn);
Btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, WebPage.class);
startActivity(intent);
final EditText username = (EditText) findViewById(R.id.LoginTx);
intent.putExtra("Username", username.getText().toString());
final EditText password = (EditText) findViewById(R.id.PasswordTx);
intent.putExtra("Password", password.getText().toString());
}
});
}
The WebPage activity code:
public class WebPage extends AppCompatActivity {
static WebView mWebView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final String user = getIntent().getExtras().getString("Username");
final String pwd = getIntent().getExtras().getString("Password");
setContentView(R.layout.activity_web_page);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
mWebView = (WebView) findViewById(R.id.webView2);
String url = "http://www.webnotes.cz/";
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.loadUrl(url);
mWebView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
view.loadUrl("javascript:document.getElementById('UID0').value = '" +user+ "';document.getElementById('PASS0').value='" +pwd+ "';javascript:document.getElementById('butLogon').click();");
}
});
}
}
In debug:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.erikstoklasa.test, PID: 13544
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.erikstoklasa.test/com.example.erikstoklasa.test.WebPage}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.os.Bundle.getString(java.lang.String)' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2626)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2695)
at android.app.ActivityThread.access$800(ActivityThread.java:173)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1552)
at android.os.Handler.dispatchMessage(Handler.java:111)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5787)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1009)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:804)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.os.Bundle.getString(java.lang.String)' on a null object reference
at com.example.erikstoklasa.test.WebPage.onCreate(WebPage.java:16)
at android.app.Activity.performCreate(Activity.java:6105)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2579)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2695) 
at android.app.ActivityThread.access$800(ActivityThread.java:173) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1552) 
at android.os.Handler.dispatchMessage(Handler.java:111) 
at android.os.Looper.loop(Looper.java:194) 
at android.app.ActivityThread.main(ActivityThread.java:5787) 
at java.lang.reflect.Method.invoke(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:372) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1009) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:804) 

WebViewClient onPageFinished never called

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.

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