1

This question seems like duplicate but I've tried with all the solutions in SO but nothing worked for me.

My question is, I want to call the javascript function from cordova java file (Which is extended from CordovaPlugin). For this I've checked reference 1, reference 2, reference 3 and many more from online but nothing worked for me

My code

Sample.js

function sendVoice() {

try {
  ApiAIPlugin.requestVoice(
    {}, // empty for simple requests, some optional parameters can be here
    function (response) {
        // place your result processing here
        alert(JSON.stringify(response));
    },
    function (error) {
        // place your error processing here
        alert(error);
    });
  } catch (e) {
    alert(e);
  }
}

Sample.java

WebView webView = new WebView(context);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient());
webView.loadUrl("javascript.sendVoice();");

I tried with simple alert in java file as below

WebView webView = new WebView(MainActivity.this);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient());
webView.loadUrl("javascript:alert('hello')");

i'm able to watch the above alert, But I'm unable to access the javascript function from java file. Any one have idea about this

Update

VoiceBotPlugin.java

package VoiceBotPlugin;

public class VoiceBotPlugin extends CordovaPlugin {

Context context;
boolean recordAudio = false;


float newX, newY, dX, dY, screenHight, screenWidth;
ImageView img;
int lastAction;
WebView webView;
public String  sJava = "String from JAVA";

@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
    if (action.equals("coolMethod")) {
        String message = args.getString(0);
        this.coolMethod(message, callbackContext);

        this.webView.loadUrl("javascript:sendVoice();");
        //this.webView.evaluateJavascript("sendVoice();", null);

        context = this.cordova.getActivity();
        ((Activity) context).runOnUiThread(new Runnable() {
              @Override
              public void run() {
                createFlotingActionButton();
              }
        });

        return true;
    }
    return false;
}

private void coolMethod(String message, CallbackContext callbackContext) {
    if (message != null && message.length() > 0) {
        callbackContext.success(message);
    } else {
        callbackContext.error("Expected one non-empty string argument.");
    }
}


private void createFlotingActionButton(){

      FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
      params.topMargin = 0;
      params.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;

      img = new ImageView(context);
      setImage(img, "icon_record");

      cordova.getActivity().addContentView(img, params);

      DisplayMetrics displaymetrics = new DisplayMetrics();
      ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
      screenHight = displaymetrics.heightPixels;
      screenWidth = displaymetrics.widthPixels;

      img.setOnTouchListener(new View.OnTouchListener() {
         @Override
         public boolean onTouch(View v, MotionEvent event) {


             switch (event.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN:

                     dX = img.getX() - event.getRawX();
                     dY = img.getY() - event.getRawY();
                     lastAction = MotionEvent.ACTION_DOWN;
                     break;

                 case MotionEvent.ACTION_MOVE:

                     newX = event.getRawX() + dX;
                     newY = event.getRawY() + dY;

                     // check if the view out of screen
                     if ((newX <= 0 || newX >= screenWidth-img.getWidth()) || (newY <= 0 || newY >= screenHight-img.getHeight()))
                     {
                         lastAction = MotionEvent.ACTION_MOVE;
                         break;
                     }

                     img.setX(newX);
                     img.setY(newY);

                     lastAction = MotionEvent.ACTION_MOVE;

                     break;

                 case MotionEvent.ACTION_UP:
                     if (lastAction == MotionEvent.ACTION_DOWN) {
                        if (recordAudio) {
                            setImage(img, "icon_record");
                            recordAudio = false;
                        } else {
                            setImage(img, "icon_mute");
                            recordAudio = true;

                            //callJavaScriptFunction();

                        }
                    }
                     break;

                 default:
                     return false;
             }
             return true;

         }
      });

  }

  private void setImage(ImageView imageView, String iconName){
      Resources activityRes = cordova.getActivity().getResources();
      int backResId = activityRes.getIdentifier(iconName, "drawable", cordova.getActivity().getPackageName());
      Drawable backIcon = activityRes.getDrawable(backResId);
      if (Build.VERSION.SDK_INT >= 16)
        imageView.setBackground(null);
      else
        imageView.setBackgroundDrawable(null);

      imageView.setImageDrawable(backIcon);
  }

  private void callJavaScriptFunction(){
    WebView webView = new WebView(context);
    webView.getSettings().setJavaScriptEnabled(true);
    webView.setWebChromeClient(new WebChromeClient());
    this.webView.loadUrl("javascript:sendVoice();");

    /*String js = String.format("window.sendVoice();", null);
    webView.sendJavascript(js);*/
  }

}

Bahu
  • 1,516
  • 2
  • 28
  • 49

2 Answers2

6

You can try something like this - cordova and webView are implicitly defined by the CordovaPlugin class:

public class MyPlugin extends CordovaPlugin {

    @Override
    public boolean execute(String action, JSONArray args,
                           CallbackContext callbackContext) throws JSONException {
        if(action.equals("foo")){
            executeGlobalJavascript("alert('hello')");
        }
        return true;
    }                       

    private void executeGlobalJavascript(final String jsString){
        cordova.getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                webView.loadUrl("javascript:" + jsString);
            }
        });
    }
}
DaveAlden
  • 30,083
  • 11
  • 93
  • 155
  • Hi DaveAlden, thank you for your reply. I've tried with your solution. It's working perfectly for `executeGlobalJavascript("alert('hello')");` but it's not working when i'm trying for `executeGlobalJavascript("sendVoice()");`. While using `sendVoice()` getting `Uncaught ReferenceError: sendVoice is not defined at :1:1` – Bahu Jan 08 '18 at 13:09
  • is `Sample.js` being loaded and is `sendVoice()` therefore actually defined? Try connecting Chrome Dev Tools to the webview and executing `sendVoice()` manually from the console. – DaveAlden Jan 08 '18 at 13:12
  • Yes `Sample.js` was loaded and `sendVoice()` is in that only. I've checked `sendVoice()` in Chrome Dev, getting error like `Uncaught ReferenceError: sendVoice is not defined`. For other details please check [VoiceBotPlugin.java](https://www.dropbox.com/s/0hd8fjwpe2vgv1v/VoiceBotPlugin.java?dl=0) (line 60 and 190 to 197) and [VoiceBotPlugin.js](https://www.dropbox.com/s/yhaukji5r7pzrp7/VoiceBotPlugin.js?dl=0) – Bahu Jan 08 '18 at 14:49
  • 2
    The problem is that you've defined `sendVoice()` within your plugin's JS component. Cordova will wrap this such that `sendVoice()` will become a private function and not global. To make it global, you'll need to explicitly assign it to the window object: `window.sendVoice = function(){` – DaveAlden Jan 08 '18 at 14:56
  • Thank you, it's worked after making my method as global and +1 for your help :-) – Bahu Jan 08 '18 at 15:10
1

For your example I think the best option is to use a callback:

So in your java you should use

PluginResult.Status status = PluginResult.Status.OK;
callbackContext.sendPluginResult(new PluginResult(status, ""));

And in your javascript you call the plugin like cordova.exec(sendVoice, null, "VoiceBotPlugin", "coolMethod", []); So when sendPluginResult is called it will execute sendVoice.

OLD: You are creating a new WebView, so it doesn't have access to your Cordova WebView, which is the one loading your javascript files.

If your class is a subclass of CordovaPlugin, you should be able to access the Cordova WebView with the webView variable.

So to call sendVoice() you should use webView.loadUrl("javascript:sendVoice();"); (note that you had javascript. and not javascript: in your example)

Also, sendVoice() should be a global function or it won't be able to find it.

If you are targetting Android 4.4 or newer you can use evaluateJavascript instead of `loadUrl``

Something like webView.getEngine().evaluateJavascript("sendVoice();", null);

Both of them should be run inside runOnUiThread as on DaveAlden answer.

jcesarmobile
  • 51,328
  • 11
  • 132
  • 176
  • Hi jcesarmobile, Thank you for your reply. I've tried with both the solution but i'm getting error like `java.lang.NullPointerException: Attempt to invoke virtual method 'void android.webkit.WebView.evaluateJavascript(java.lang.String, android.webkit.ValueCallback)' on a null object reference` and `java.lang.NullPointerException: Attempt to invoke virtual method 'void android.webkit.WebView.loadUrl(java.lang.String)' on a null object reference` – Bahu Jan 08 '18 at 12:46
  • Can you share your whole .java class? – jcesarmobile Jan 08 '18 at 12:48
  • Please check my updated question. (I've used your suggested solution in `execute` method) – Bahu Jan 08 '18 at 12:53
  • I've changed according to you but not getting expected result. Something wrong i'm doing. Can you please check these [VoiceBotPlugin.js](https://www.dropbox.com/s/3rfkrw0jtvef1fh/VoiceBotPlugin.js?dl=0) (line 20) and [VoiceBotPlugin.java](https://www.dropbox.com/s/mxjk9nf1e9oyufd/VoiceBotPlugin.java?dl=0) (line 61 and 62) – Bahu Jan 08 '18 at 14:05
  • In your code it should be `exec` instead of `cordova.exec` as you are doing the `var exec = require('cordova/exec');` – jcesarmobile Jan 08 '18 at 14:08
  • Initially i've tried for this but didn't get any response (even log also not showing anything) – Bahu Jan 08 '18 at 14:14
  • Looking into your code, you are not really calling the cordova.exec part, that is just outside everything. Remove all that and wherever you are calling `coolMethod` in javascript, just use sendVoice as the success callback instead of what you have now – jcesarmobile Jan 08 '18 at 14:18