Android Phonegap: Notify javascript when an AsyncTask is finished - javascript

in my app, when user click on a button in webview, a phonegap plugin will be called to trigger an asynctask to download file from internet. Now i want to send a signal back to javascript part when the asynctask is finished. But i don't know how to do it, because my plugin had already send something back before the asynctask is finished. Does anyone know how i can notify my javascript part without plugin in Phonegap?

I also asked this question in Phonegap Google Group, here is response of Simon Mac Donald. It works perfectly for me:
You can handle this situation by using the Plugin API quite easily. It
is implemented in the core API items Connection and Battery. What you
need to do is:
1) In your execute() method of your plugin save the callbackId you get.
2) Return a NO_RESULT plugin result and set keep callback id to true.
PluginResult pluginResult = new PluginResult(PluginResult.Status.NO_RESULT);
pluginResult.setKeepCallback(true);
return pluginResult;
3) When you async java method finishes return another plugin result like this:
PluginResult result = new PluginResult(PluginResult.Status.OK, data);
result.setKeepCallback(false);
this.success(result, this.myCallbackId);
As I said, you can look at the code in GitHub to see how we are using this for Connection and Battery.

This is how I solve problems like your.
1) Create and associate a JavascriptInterface to your WebView. A JavascriptInterface is simply a class inside which you can declare some Java method you want to use from JS.
public class JSInterface() {
private final CountDownLatch latch = new CountDownLatch(1);
public void waitForProceed() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void canProceed() {
latch.countDown();
}
}
2) In your AsyncTask, at the end of onPostExecute() method, you have to call the canProceed() method to notify to JSInterface that it can exit from waitForProceed() method.
public class MyAsyncTask extends AsyncTask<.......> {
private JSInterface jsi;
... // other class property
public MyAsyncTask(JSInterface jsi) {
...
//do what you want with other class property
this.jsi = jsi;
}
#Override
public ... doInBackground(...) {
...
//do something
}
#Override
public void onPostExecute(...) {
...
//do something
jsi.canProceed();
}
}
3) In your Activity you have to associate the JSInterface object to your WebView:
WebView mWebView;
...
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new JSInterface(), "JSIface");
4) Finally, in JS, you can call AsyncTask (I don't know how you call it, but I guess you use somthing like a JSInterface) and after call waitForProceed() method:
startAsyncTask(); //somehow
JSIface.waitForProceed();
I hope it solves your problem ;)

Here is a detailed example:
Lets create some interface that should be called when AsyncTask will finish the stuff, a.e when onPostExecute called.
In my case we fetch some JSONArray data
MyTaskListenerItf.java
public interface GroupTaskListenerItf {
public void onTaskDone(JSONArray groupArray);
}
The AsyncTask template of looks like:
MyBuildTask.java
public class MyBuildTask extends AsyncTask<Void, Void, SomeData>{
private MyTaskListenerItf mTl = null;
public MyBuildTask(Context context, MyTaskListenerItf tl) {
super();
this.mContext = context;
this.mTl = tl;
}
#Override
protected SomeData doInBackground(Void... params) {
/* ... */
}
#Override
protected void onPostExecute(WmTransferItem transferItem) {
// ...
if(this.mTl != null){
JSONArray data = new JSONArray("");
this.mTl.onTaskDone(data);
}
// ..
}
}
So now our CordovaPlugin class should look like:
MyCordovaPlugin.java
public class MyCordovaPlugin extends CordovaPlugin implements GroupTaskListenerItf {
// we need this callback when Task will finish
private CallbackContext mMyCallbackContext = null;
#Override
public boolean execute(String action, JSONArray args,CallbackContext callbackContext) throws JSONException {
if("runMe".equals(action)){
final GroupTaskListenerItf gt = this;
mMyCallbackContext = callbackContext;
// pass our 'GroupTaskListenerItf' interface to async class
MyBuildTask task = new MyBuildTask(cordova.getActivity(), gt);
task.execute();
PluginResult pluginResult = new PluginResult(PluginResult.Status.NO_RESULT);
pluginResult.setKeepCallback(true);
callbackContext.sendPluginResult(pluginResult);
}
else{
this.cordova.getThreadPool().execute( new Runnable() {
public void run() {
// BTW, here you might run something else out of UI Thread
}
});
}
}
/* ... */
#Override
public void onTaskDone(JSONArray data) {
if (this.mGroupCallbackContext != null) {
PluginResult result = new PluginResult(PluginResult.Status.OK, data);
result.setKeepCallback(false);
this.mMyCallbackContext.sendPluginResult(result);
}
}
That's all.
Hope it will help to someone.

Related

Call Wicket 6 Code from Javascript and return value

I have managed to call my Wicket 6 Java code from Javascript using option A in this example: https://stackoverflow.com/a/42612027/1047418
However, I have not been able to find examples for returning data from the Java side back to JavaScript (the generated JavaScript callback function does not even include a return statement). How can this be achieved?
Edit: I am not trying to set an attribute in Java and as I've already explained, calling Wicket from JavaScript is not the problem here. I am trying to return a JSON object from Wicket back to the browser as a result of an Ajax request.
Edit2: Following martin-g's examples I cobbled up this working example...
Java
public class MyAjaxBehaviour extends AbstractDefaultAjaxBehavior {
#Override
protected void onComponentTag(ComponentTag tag) {
super.onComponentTag(tag);
tag.put("aprachatcallbackurl", getCallbackUrl());
}
#Override
protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
super.updateAjaxAttributes(attributes);
attributes.setDataType("json");
attributes.setWicketAjaxResponse(false);
}
#Override
protected void respond(AjaxRequestTarget target) {
getComponent().getRequestCycle().replaceAllRequestHandlers(
new TextRequestHandler("application/json", "UTF-8", "{...JSON GOES HERE...}));
}
}
JavaScript
var mySuccessCallback = function(param1, param2, data, statusText) {
// Data contains the parsed JSON object from MyAjaxBehaviour.respond(...)
...
}
var myFailureCallback = function() {
...
}
Wicket.Ajax.get({
"u": callbackUrl,
"dt": "json",
"wr": false,
"sh": [mySuccessCallback],
"fh": [myFailureCallback]
});
Main problem as that the Wicket 7 Reference incorrectly instructs to use "wr" instead of "dt" in the JavaScript call. :)
I think you can do it in a simpler way!
Wicket Ajax API is just: Wicket.Ajax.ajax({...}). All you need to prepare at the server side is to save the callback url, e.g. by saving it globally in the window object or in HTML element's attributes (data-the-url).
public class CallFromJavascriptBehavior extends AbstractDefaultAjaxBehavior {
#Override
protected void respond(AjaxRequestTarget target) {
final StringValue parameterValue = RequestCycle.get().getRequest().getQueryParameters().getParameterValue("yourName");
System.out.println(String.format("Hello %s", parameterValue.toString()));
// write anything to the WebResponse and then consume it in the JS success handler. See below
}
#Override
public void onComponenntTag(ComponenntTag tag, Component component) {
super.onComponenntTag(tag, component);
tag.put("data-the-url", getCallbackUrl());
}
}
Then in your JS code you can do:
var callbackUrl = jQuery("#theElementId").data("the-url");
Wicket.Ajax.get({"u": callbackUrl, "sh":[successHandler], "fh": [failureHandler] });
Where successHandler and failureHandler are JS functions defined inline (e.g. function(...) {}) or elsewhere.
More documentation you can find at:
https://ci.apache.org/projects/wicket/guide/7.x/single.html#_ajax_request_attributes_and_call_listeners
A blog article with an complete example at http://wicketinaction.com/2012/07/wicket-6-javascript-improvements/
You can just write a Resource and mount it, and get it with your favorite Ajax-approach.
For example:
public class MyResource extends AbstractResource
#Override
protected ResourceResponse newResourceResponse( Attributes attributes )
{
ResourceResponse resourceResponse = new ResourceResponse();
resourceResponse.setContentType( "text/json" );
resourceResponse.setTextEncoding( "utf-8" );
HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
try
{
this.json = IOUtils.toString( request.getInputStream() );
}
catch ( IOException e )
{
e.printStackTrace();
}
resourceResponse.setWriteCallback( new WriteCallback()
{
#Override
public void writeData( Attributes attributes ) throws IOException
{
OutputStream outputStream = attributes.getResponse().getOutputStream();
Writer writer = new OutputStreamWriter( outputStream );
writer.write( MyResource.this.json );
writer.close();
}
} );
return resourceResponse;
}
(Copied from my other answer here https://stackoverflow.com/a/17876029/461499)
And see here for mounting it:
https://dzone.com/articles/how-implement-rss-feeds-custom

How can I call a server side c# method via javascript without a WebMethod or Updatepanel?

I would prefer not to use an update panel and using the common WebMethod approach leads me to this error with this code
private string currentHtml() {
StringWriter str_wrt = new StringWriter();
HtmlTextWriter html_wrt = new HtmlTextWriter(str_wrt);
Page.RenderControl(html_wrt);
return str_wrt.ToString();
}
[WebMethod]
public static void EmailPtoRequest() {
string test = currentHtml();
}
Error 8 An object reference is required for the non-static field, method, or property 'PtoRequest.cs.WebForm1.currentHtml()
Clearly the method being static is causing a bunch of headaches.
Is there a standard that I can use for this type of functionality? The goal is to allow the user to send their data to the server without causing a post and refreshing the page.
Note: I DO NOT want to use a webmethod as it is causing an error which does not let me compile.
public partial class WebForm1 : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e) {
}
private string currentHtml() {
StringWriter str_wrt = new StringWriter();
HtmlTextWriter html_wrt = new HtmlTextWriter(str_wrt);
Page.RenderControl(html_wrt);
return str_wrt.ToString();
}
[WebMethod]
public static void EmailPtoRequest() {
WebForm1 WebForm1 = new WebForm1();
string test = WebForm1.currentHtml();
}
}
Results in 'test' being an empty string instead of the html of the page.
private static string currentHtml() {
StringWriter str_wrt = new StringWriter();
HtmlTextWriter html_wrt = new HtmlTextWriter(str_wrt);
Page.RenderControl(html_wrt);
return str_wrt.ToString();
}
[WebMethod]
public static void EmailPtoRequest() {
string test = currentHtml();
}
}
Results in the first error again, but in the currentHtml method instead.
Please remember the question is not about the error, but an alternative to webmethod or update panels. Thank you.
3 options:
Make the currentHtml method static,
Instantiate the class that contains currentHtml like this:
new MyClass().currentHtml();
Use an ajax enabled wcf service.

Asynchronous JSON String loading JavaScript

I'm using the d3 force directed graph to display some data I get from an API. Before I can display it, it runs through a java class, which does write it into the right json format.
Since the programm runs in a JavaFX WebView I have a bridge class, that does have a getter method I can call from the JavaScript.
In my Main class I create a WebView and assign the bridge to it. I initialize my JSON translator and pass the bridge to it.
#Override
public void start(Stage stage) {
try {
new JsonTranslator(individual, depth, bridge);
Scene scene = createScene();
[...]
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
private Scene createScene() {
[...]
try {
JSObject jsobj = (JSObject) webEngine.executeScript("window");
jsobj.setMember("java", bridge);
} catch (Exception e) {
e.printStackTrace();
}
[...]
}
In my JSONTranslator class I write the json and pass it to the bridge
private void writeFile() {
try {
bridge.setJSONObject(obj.toJSONString());
FileWriter file = new FileWriter(
"C://path/to/some/file.json"
file.write(obj.toJSONString());
file.flush();
file.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Object:" + obj);
}
I also write it to a file. The data gets printed as expected. Now in my bridge the data is available throughout the getter / setter. In JSToFXBridge.java:
public String getJSONObject(){
System.out.println("get request: " + json);
return json;
}
public void setJSONObject(String string){
this.json = string;
}
Now I call it from my JavaScript
[...]
var draw = function(json, callback) {
[...]
callback.call(data);
};
var data = java.getJSONObject();
draw(data);
However it does print get request: -my json data- on the console, the json string is compleatly fine. If I copy & paste it from the console to be like this var data = -my json data- in the code it works. Only to asign it directly from the method won't work. I can't figure out why since I try to load it asynchronously. Based on this tutorial. Do I make a mistake in laoding the string? Or is it even a wrong way to do so?
Good answer / tutorial to asynchronous JavaScript callbacks can be found here. Solution, which created a new problem [ solved as well ], provided here.
In general think of this pattern:
function addOne(thenRunThisFunction) {
waitAMinuteAsync(function waitedAMinute() {
thenRunThisFunction()
})
}
addOne(function thisGetsRunAfterAddOneFinishes(){})
Explains it very well

Wicket 6.2 AbstractDefaultAjaxBehavior getCallbackUrl no longer resolves JS variables

Recently I have been working on upgrading a big web application that was using wicket 1.4.18 to 6.2. We had a situation where we would create javascript variables to keep track of positioning within a drag and drop list. This is just the wicket side of the code since the js has always worked and has not been changed.
ListItem.add(new AbstractDefaultAjaxBehavior()
{
private static final long serialVersionUID = 1L;
#Override
public void onComponentTag(ComponentTag tag)
{
tag.put("ondrop", "var value = $(ui.item[0]).attr('hiddenvalue');"
+ this.getCallbackScript());
}
#Override
public final CharSequence getCallbackUrl()
{
return super.getCallbackUrl() + "&hiddenvalue' + value + '";
}
}
However the problem I am running into is the javascript variables are not resolving to values and are now being taken as literal strings (Ex: 'value' instead of 5) in the getCallbackUrl. This was not the case in wicket 1.4.18 and I don't believe this problem originated in our migration to 1.5.8.
In the end we just want to be able to pull the value out using
#Override
protected void respond(AjaxRequestTarget target)
{
getRequest().getRequestParameters().getParameterValue("hiddenvalue");
}
Any advice on this? I hope I have provided enough information.
Thanks in advance for any help. Some of this is a little beyond my knowledge and can be intimidating not knowing where to look.
Wicket Ajax has been completely rewritten for Wicket 6. See this page for a detailed description.
In your case, you should use the new AjaxRequestAttributes like that:
#Override
protected void updateAjaxAttributes(final AjaxRequestAttributes attributes) {
super.updateAjaxAttributes(attributes);
attributes.getExtraParameters().put("hiddenvalue", "value");
}
Retrieval of the value from the request still works the same as before.
#Override
protected void respond(AjaxRequestTarget target)
{
getRequest().getRequestParameters().getParameterValue("hiddenvalue");
}
Another cleaner approach is to use the callback function
AbstractDefaultAjaxBehavior ajaxBehavior = new AbstractDefaultAjaxBehavior() {
#Override
protected void respond(AjaxRequestTarget target) {
String param1Value = getRequest().getRequestParameters().getParameterValue(AJAX_PARAM1_NAME).toString();
String param2Value = getRequest().getRequestParameters().getParameterValue(AJAX_PARAM2_NAME).toString();
System.out.println("Param 1:" + param1Value + "Param 2:" + param2Value);
}
#Override
public void renderHead(Component component, IHeaderResponse response) {
super.renderHead(component, response);
String callBackScript = getCallbackFunction(CallbackParameter.explicit(AJAX_PARAM1_NAME), CallbackParameter.explicit(AJAX_PARAM2_NAME)).toString();
callBackScript = "sendToServer="+callBackScript+";";
response.render(OnDomReadyHeaderItem.forScript(callBackScript));
}
};
add(ajaxBehavior);
Define a variable for the function in your javascript
var sendToServer;
It will be initialized on dom ready event by wicket with the callback function
Call sendToServer(x,y) from javascript to pass the parameters to the server.
private static final String MY_PARAM = "myparam";
public static class SampleCallbackBehavior extends AbstractDefaultAjaxBehavior {
#Override
public void renderHead(Component component, IHeaderResponse response) {
super.renderHead(component, response);
response.render(OnDomReadyHeaderItem.forScript("var myfunction : " + getCallbackFunction(CallbackParameter.explicit(MY_PARAM))));
}
#Override
protected void respond(AjaxRequestTarget target) {
StringValue paramValue = getComponent().getRequest().getRequestParameters().getParameterValue(MY_PARAM);
//TODO handle callback
}
}
After this, you should only call the function from javascript
myfunction("paramValue");

How do I assign a JSONObject or JavaScriptObject to $wnd[x] in GWT?

I read some var from my HTML-Page using GWT Dictionary. The var looks like this:
var test = {
"a" : "123",
"b" : "jg34l",
...
}
Now I get via AJAX-Call new content for my JS var. At the moment I overwrite it like this:
public native void set(String key, String value) /*-{
$wnd["test"][key] = value;
}-*/;
public void onResponseReceived(Request request, Response response) {
JSONObject obj = (JSONObject) JSONParser.parseLenient(response.getText());
for (String key : obj.keySet()) {
JSONString val = (JSONString) obj.get(key);
set(key, val.stringValue());
}
}
As you can see I get a JSON-String. Parse it. Cast it to JSONObject. Take every key-value-pair and use the JSNI-method to set the pairs.
There must be an easier way to do this?! I want simply to say: $wnd["test"] = myJsonObject
Please help me, 'cause it is performance-critical step (up to 1000 key-value-pairs).
You can try using JSO (should be also much faster than JSONObject)
Something like this:
public native void set(JavaScriptObject data) /*-{
$wnd['test'] = data;
}-*/;
public void onResponseReceived(Request request, Response response) {
JavascriptObject data = JsonUtils.safeEval(response.getText()).case();
set(data);
}
I haven't tried myself, so I can't say 100% if it would work.
Update:
Instead of a Dictionary you can directly return a JavaScriptObject that supports hash like access:
public class MyDictionary extends JavaScriptObject {
public native String get(String name) /*-{
return this[name];
}-*/;
}
public native MyDictionary getData() /*-{
return $wnd['test'];
}-*/
Here's my version for GWT Dictionary (true dynamic language switch):
public native void setTranslation(String locale, JavaScriptObject translationData) /*-{
$wnd[locale] = translationData;
}-*/
#Inject
TranslationProvider translationProvider;
public void onResponseReceived(Request request, Response response) {
String newLocale = "en";
JavaScriptObject translationData = JsonUtils.unsafeEval(response);
setTranslation(newLocale, translationData);
translationProvider.setLocale(newLocale);
LocaleChangeEvent.fire(eventBus);
}
Every Widget that displays translatable text registers to LocaleChangeEvent and implements LocaleChangeEventHandler:
public interface LocaleChangeEventHandler extends EventHandler {
void onLocaleChange(LocaleChangeEvent event);
}
In the onLocaleChange method it simply calls the get(...) method from the TranslationProvider class to receive its String:
public class LocaleAwareLabel extends Label implements LocaleChangeEventHandler {
TranslationProvider translationProvider;
#Inject
public LocaleAwareLabel(TranslationProvider translationProvider, EventBus eventBus) {
this.translationProvider = translationProvider;
eventBus.addHandler(LocaleChangeEvent.TYPE, this);
get();
}
public void get() {
this.setText(translationProvider.get("myDictionaryKey"));
}
#Override
public void onLocaleChange(LocaleChangeEvent event) {
get();
}
}
And here the TranslationProvider class:
#Singleton
public class TranslationProvider {
Dictionary dictionary;
public void setActiveLocale(String locale) {
dictionary = Dictionary.getDictionary(locale);
}
public String get(String key) {
dictionary.get(key);
}
}
That's it!

Categories

Resources