I am trying to make a phonegap/cordova plugin which is for converting speech to text using RecognizerIntent. The following is my code of the plugin class:
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
this.callbackContext = callbackContext;
try {
if (ACTION_INVOKE_SPEECH_RECOG.equals(action)) {
//JSONObject arg_object = args.getJSONObject(0);
Intent calIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
calIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, "en-US");
this.cordova.setActivityResultCallback(SpeechToTextPlugin.this);
this.cordova.getActivity().startActivityForResult(calIntent,REQUEST_CODE );
callbackContext.success("Completed Main Activity");
}
callbackContext.error("Invalid action");
return false;
} catch(Exception e) {
System.err.println("Exception: " + e.getMessage());
callbackContext.error(e.getMessage());
return false;
}
}
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (resultCode == Activity.RESULT_OK) {
JSONObject obj = new JSONObject();
try {
//obj.put("TEXT", intent.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS).toString());
callbackContext.success(intent.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS).toString());
}catch(JSONException e){
}
super.onActivityResult(requestCode, resultCode, intent);
}
}
I pass the recognized text to the javascript invoking the execute method. Right now, after making a call to startActivityForResult, the execute function returns back. How do I pass the recognized word to the js?
You have to use
sendJavascript("yourJSClass.yourJSMethod();");
or just
sendJavascript("yourJSMethod();");
Of course you will have to have:
yourJSMethod = function() {}
in your JS
Related
public static String dataFromJs;
public void androidMethod() {
// initialize static String dataFromJs.
dataFromJs = "";
webView.evaluateJavascript("javascript:jsFuction();", new ValueCallback<String>() {
#RequiresApi(api = Build.VERSION_CODES.O)
#Override
public void onReceiveValue(String s) {
JsonReader reader = new JsonReader(new StringReader(s));
reader.setLenient(true);
try {
if (reader.peek() != JsonToken.NULL) {
if (reader.peek() == JsonToken.STRING) {
// get string value from javascript and I want to use the value
dataFromJs = reader.nextString();
}
}
} catch (IOException e) {
Log.e("TAG", "MainActivity: IOException", e);
} finally {
try {
reader.close();
} catch (IOException e) {
// NOOP
}
}
}
});
#RequiresApi(api = Build.VERSION_CODES.KITKAT)
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if(url.endsWith("menu/page.do")) {
androidMethod();
// do something with dataFromJs..
}
}
The androidMethod is my method in android.
I try to use the str what I receieved from javascript.
but webView.evaluateJavascript method always return null at the first time,
the webView.evaluateJavascript is called again and return string value correctly.
Since the return value is null at the first time, so I can't use the androidMethod in another method.
Anyone has good solution??
is it have something to do with this log (The application may be doing too much work on its main thread.) ?
Thank you!
We can run simple javascript function (jsCode) like:
function() {
return "test"
}
in webview:
String js = "(" + jsCode + ") ();"
mWebView.evaluateJavascript(js, new ValueCallback<String>() {
#Override
public void onReceiveValue(String s) {
Log.d("return value: ", s); // Returns the value from the function
}
});
But what if my javascript depends on another module or reference another file like:
import { double } from 'mymodule';
Can this be achieved?
I achieved this by evaluating the external javascript file before evaluating the jsCode
private String[] jsFiles = {"external_lib.js", "main.js"};
private void evaluateJS() {
try {
String js = readFromAssets(jsFiles[fileIndex]);
Log.e(TAG, "evaluating: " + jsFiles[fileIndex]);
mWebView.evaluateJavascript(js, new ValueCallback<String>() {
#Override
public void onReceiveValue(String s) {
fileIndex++;
if (fileIndex == jsFiles.length) {
// we have evaluated the last javascript file
Log.e("return value: ", s); // Returns the value from the function
return;
}
evaluateJS();
}
});
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
the code in main.js uses variables and functions that are defined in external_lib.js
MainActivity.java
public String URI = null;
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 0 && resultCode == RESULT_OK)
{
Uri pickedImage = data.getData();
URI = pickedImage.toString();
}
}
public String GetURI()
{
return URI;
}
WebAppInterface.java
#JavascriptInterface
public void GetPicture()
{
Intent galleryIntent = new Intent(Intent.ACTION_PICK);
galleryIntent.setType("image/*");
((MainActivity) mContext).startActivityForResult(galleryIntent, 0);
}
#JavascriptInterface
public String GetURI()
{
return getRealPathFromURI(mContext, Uri.parse(((MainActivity) mContext).GetURI()));
}
I'm using the following Javascript functions
function GetPicture()
{
Android.GetPicture();
}
function loadImage()
{
document.getElementById("img").src = Android.GetURI();
}
The first one is used to get the picture and store the full path in a string. The second function is used to obtain the the full path from the string.
Currently this is working because I'm first calling GetPicture and then loadImage.
But I want to "merge" the functions.
So the Javascript would be:
function loadImage()
{
document.getElementById("img").src = Android.GetPicture();
}
So I changed the code from WebAppInterface.java to this:
#JavascriptInterface
public String GetPicture()
{
Intent galleryIntent = new Intent(Intent.ACTION_PICK);
galleryIntent.setType("image/*");
((MainActivity) mContext).startActivityForResult(galleryIntent, 0);
return ((MainActivity) mContext).GetURI();
}
But it's not working. I think I know why because GetURI is being called before the string URI has been set.
So how do I fix this problem?
If somebody knows a better title please let me know.
OK I think I found a solution but I don't know if this is the best way to do it.
So for those who want to know how I fixed it:
I changed the GetPicture method
#JavascriptInterface
public String GetPicture()
{
Intent galleryIntent = new Intent(Intent.ACTION_PICK);
galleryIntent.setType("image/*");
((MainActivity) mContext).startActivityForResult(galleryIntent, 0);
while(((MainActivity) mContext).IsFinished == false){}
return ((MainActivity) mContext).GetURI();
}
In MainActivity.java I added a public boolean called IsFinished (set to false) and it is going to be true when the user picked an image (onActivityResult).
So the code will be:
public String URI = null;
public boolean IsFinished = false;
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 0 && resultCode == RESULT_OK)
{
Uri pickedImage = data.getData();
URI = pickedImage.toString();
IsFinished = true;
}
}
public String GetURI()
{
return URI;
}
I have written a native class to download from the internet, and I want to call that code from JavaScript. I can call the native code from JavaScript in webview, but I don't know how to call the native event in JavaScript.
This is the native code :
public class DonwloadTask extends AsyncTask<URL,Void,Long> {
private OnTaskCompleted listener;
private String result;
public DonwloadTask(OnTaskCompleted listener){
this.listener=listener;
}
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
StringBuilder resultBuilder = new StringBuilder();
for (int i = 0; i < count; i++) {
try {
// Read all the text returned by the server
InputStreamReader reader = new InputStreamReader(urls[i].openStream());
BufferedReader in = new BufferedReader(reader);
String resultPiece;
while ((resultPiece = in.readLine()) != null) {
resultBuilder.append(resultPiece);
}
in.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// if cancel() is called, leave the loop early
if (isCancelled()) {
break;
}
}
// save the result
this.result = resultBuilder.toString();
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
// update progress here
}
// called after doInBackground finishes
protected void onPostExecute(Long result) {
//Log.v("API", this.result);
// put result into a json object
try {
JSONObject jsonObject = new JSONObject(this.result);
// call callback
listener.onTaskCompleted(jsonObject);
} catch (JSONException e) {
e.printStackTrace();
}
}
</code>
And this is interface for event
<code>
public interface OnTaskCompleted {
String onTaskCompleted(JSONObject result);
}
</code>
And this is class wrapped the DownloadTask
<code>
public class NativeApi implements OnTaskCompleted {
private DonwloadTask task;
public NativeApi(){
task= new DonwloadTask(this);
}
public void startDownload(String urlStr){
URL url= null;
try {
url = new URL(urlStr);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
#Override
public String onTaskCompleted(JSONObject result) {
return result.toString();
}
}
</code>
And javascript to call native code
<code>
function startDownload(url){
NativeApi.startDownload(url);
}
</code>
How can I subscribe onTaskCompleted in JavaScript?
You can make a call as follws:
MyActivity.activity.sendJavascript('yourFunction();');
Check this link for further info Calling javascript function from the android activity
In my android application, i have called one javascript method from html page to java page.
this.start = function ()
{
try
{
Mynamespace.myapi.getvalue("myvalue", {
onSuccess: function (data)
{
value= JSON.parse(data);
alert("called");
},
onFailure: function (error) { }
});
}
catch (err) { }
if (value== null) { value= new Object();
};
In the above, getvalue method is called in my java class and returned some values, but before getting the value inside the OnSuccess/OnFailure, the below line is called. How to wait the method up to the callback is called?
if (value== null) { value= new Object();
private class LongOperation extends AsyncTask<String, Void, String>
{
protected void onPreExecute()
{
progressDialog = new ProgressDialog(activity.this);
progressDialog.setTitle("Processing...");
progressDialog.setMessage("Please wait...");
progressDialog.setCancelable(true);
progressDialog.show();
}
protected String doInBackground(String... params)
{
try
{
//Getting data from server
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(String result)
{
progressDialog.dismiss();
}
}
How to call this
ProgressDialog progressDialog;
LongOperation mytask = null;
mytask = new LongOperation();
mytask.execute();
I have analyzed more about my query. Finally i got a solution. String that i have loaded with webview was having more single quotes so unable to load with webview. So instead of calling Success failure callback, the next line is called.
Now I have replaced the single quotes "\'" by "\'" in my java class. Working fine. :-)