Call javascript method from Cordova Plugin's service - javascript

I am currently developing an App using Cordova and therefore have developed a Cordova Plugin, that runs a service and is being started from within that plugin:
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
switch(action){
case "start":
Intent startIntent = new Intent(context, BackgroundService.class);
startIntent.putExtra("action", "play");
context.startService(startIntent);
return true;
}
return false;
}
The code is shortened for readability. Now normally I would use callbacks to call javascript methods from within the plugin, but as I want to use a service for the functionality, how could I call a method (or maybe the callback) from there?
Thank you very much in advance :).

Okay so I did not find a way to this with vanilla cordova, but this Plugin provides the functionality I was looking for: https://github.com/bsorrentino/cordova-broadcaster
You have to set up a LocalBroadcastManager in your native Android Code and send an Intent with it. You then define a bundle containg the data you want to send, and put it as extra of your intent. Then you send the intent via the broadcast manager and receive it via javascript.
Sample Java:
startCallback = new Intent("callback");
Bundle b = new Bundle();
b.putString("callback", "start");
startCallback.putExtras(b);
LocalBroadcastManager.getInstance(applicationContext).sendBroadcastSync(startCallback);
Sample Javascript:
var callbackListener = function( e ) {
console.log("What kind of callback: " + e.callback);
};
window.broadcaster.addEventListener( "callback", callbackListener);
I hope this helps somebody with a similiar problem :).

Related

Call Swift function from Cordova

I have a native iOS app written in Swift and it works on standalone. However, currently, I am exploring Cordova and developing a plugin for it using the Swift codes that I already have. Let's say my native Swift code is the following and the function that I want to call is startDataLog():
func startDataLog() {
// Set file logging parameters and do some logging etc...
let fileName = "log_\(dateFormatter.string(from: Date())).log"
let filePath = URL(fileURLWithPath:
(NSSearchPathForDirectoriesInDomains(.documentDirectory,
.userDomainMask, true)[0] as
NSString).appendingPathComponent(fileName))
//do other stuffs here...
}
Assuming that the plugin.xml and the bridging header are correctly configured for the new plugin:
First, I need to add this in www/plugin.js that calls the iOS codes. In my case startDataLog:
var exec = require('cordova/exec');
var serviceName = 'DriverAppPlugin'
module.exports = {
'startDataLog': function (successCallback, errorCallback) {
cordova.exec(successCallback, errorCallback, serviceName,
'startDataLog', [])
},
}
Then in my native Swift code I will have to add the following:
#objc(CentralPlugin) class CentralPlugin: CDVPlugin {
func startDataLog(command: CDVInvokedUrlCommand) {
// Set file logging parameters and does some extras
let fileName = "log_\(dateFormatter.string(from: Date())).log"
let filePath = URL(fileURLWithPath:
(NSSearchPathForDirectoriesInDomains(.documentDirectory,
.userDomainMask, true)[0] as
NSString).appendingPathComponent(fileName))
//do other stuffs here...
}
}
What is unclear to me is if this is what needs to be done to the native Swift code or the function that I want to call? Is adding
(command: CDVInvokedUrlCommand)
to the func is correct or am I doing something terribly wrong here. I am new to Cordova and as a matter of fact, there are not many tutorials that are based on integrating Cordova + Swift.
Any feedback or directions will be really helpful to kickstart my plugin development. Please suggest.

How to pass a variable from javascript to objective C and Objective C to Javasctipt in ios

I am a iOS Developer, am new to javascript.
I wants to create a communication between Javascript to Objective C and Objective C to Javascript.
How to pass a variable from javascript to objective C and Objective C to Javasctipt in ios.
If any one have references please let us know about this?
I program JavaScript when using Parse CloudCode. In my case, I use CloudCode to call the Stripe API.
For example:
Objective-C
NSDictionary *parameters = #{
#"customerId": stripeCustomerId,
#"amount": #(100),
};
[PFCloud callFunctionInBackground:#"chargeCustomer" withParameters:parameters block:block];
JavaScript
Parse.Cloud.define("chargeCustomer", function(request, response) {
Stripe.Charges.create({
amount: request.params.amount, // in cents
currency: "usd",
customer: request.params.customerId,
},{
success: function (httpResponse) {
console.log(httpResponse);
response.success(httpResponse);
},
error: function (httpResponse) {
console.error(httpResponse.message);
response.error(httpResponse.message);
}
});
});
As you can see, to pass on the variable from objective-c to javascript in this case, you use request.params.
If you are targeting iOS8, you could consider to use WKWebView rather than UIWebView, which has considerably improved in this regard. Good starting points are the the WKWebView reference docs and this NSHipster article.
You can use native iOS objects to do this.
From Obj-C to JS
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script
It injects or calls pre-existing method/object in the page loaded in the UIWebview. It returns the value that JS return to you
UIWebView Class Reference
From JS to Obj-C
Implementing UIWebView protocol you can handle the request before it will start:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest (NSURLRequest *)request navigationType (UIWebViewNavigationType)navigationType
So, from JS you have to call a URL with a custom schema http such as test://. Then you will parse the request in the delegate method I wrote before.

Pushwoosh phonegap plugin, retrieving hwid on ios

I'm using pushwoosh to send push notifications to my ios mobile app. I want to allow users to disable notifications from within the app. The problem I'm having is that the pushwoosh api uses a different device id for ios than it does for android. The device id is created by the plugin using native code. It uses the hardware mac address and applies the md5 algorithm to create a "unique" id that phonegap is calling "hwid"(hardware id). I've found the native, objective c class that does this but I don't know how to access the variable, "hwid", from Javascript.
I've read through the phonegap documentation and have created a plugin that allows me to access native ios classes. My problem is that I don't know objective c and therefore cannot figure out how to return the variable to the callback.
The pushwoosh api requires the device id in order to unregister a device as you can see here:
{
"request":{
"application":"APPLICATION_CODE",
"hwid": "hardware device id"
}
}
I have seen this post and it is not helpful for what I'm trying to accomplish. However, it does show the native code that creates the unique id.
I also found this class that prints the hwid to the console. If I could find a way to access the "hwid" below from my js code I would be all set.
#import "PWRequest.h"
#implementation PWRequest
#synthesize appId, hwid;
- (NSString *) methodName {
return #"";
}
//Please note that all values will be processed as strings
- (NSDictionary *) requestDictionary {
return nil;
}
- (NSMutableDictionary *) baseDictionary {
NSMutableDictionary *dict = [NSMutableDictionary new];
[dict setObject:appId forKey:#"application"];
[dict setObject:hwid forKey:#"hwid"];
NSLog(#"hwid: %#", hwid);
return [dict autorelease];
}
- (void) parseResponse: (NSDictionary *) response {
}
- (void) dealloc {
self.appId = nil;
self.hwid = nil;
[super dealloc];
}
#end
Can someone point me in the right direction? Thanks.
We have just added unregisterDevice method for iOS Phonegap Javascript.
PushNotification.prototype.unregisterDevice = function(success, fail) {
cordova.exec(success, fail, "PushNotification", "unregisterDevice", []);
};
It used to work only for Android, now it is available on iOS as well.
For Phonegap 3.0 please see the newest Pushwoosh plugin repo:
https://github.com/shaders/pushwoosh-phonegap-3.0-plugin
For older Phonegap versions <= 2.9 please see legacy Pushwoosh Phonegap plugin:
https://github.com/shaders/phonegap-cordova-push-notifications/tree/master/iOS
I hope it helps!
I found a work-around for anyone who needs this. Just open up the class "PWRequest.m" in xcode. Add the code below just under "[dict setObject:hwid forKey:#"hwid"];" in the NSMutableDictionary method.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"hwidfile2.txt"];
NSLog(#"From Echo Class File Path: %#", filePath);
NSString *str = hwid;
This will save a text file to your local app directory in which you can access from your Javascript code. For example, you can use this JS code to access and print the hwid to the console. Just call the 'readPwfile(filename)' function, passing in the name of your file as the function argument.
function readPWFile(fileName){
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSystem){
fileSystem.root.getFile(fileName, null, gotReadFileEntry, fail);
});
function gotReadFileEntry(fileEntry) {
fileEntry.file(gotFile, fail);
}
function gotFile(file){
//readDataUrl(file);
readAsText(file);
}
function readAsText(file) {
var reader = new FileReader();
reader.onloadend = function(evt) {
console.log('Reading file... hwig Result: '+evt.target.result);
};
reader.readAsText(file);
}
}

How to link to Windows 8 Phone Store App review for my app?

I don't want to open the browser but the actual store in my Windows 8 phone.
I am developing an app using PhoneGap and so I want to do this with Javascript.
I haven't submitted my app so I don't yet have a package name. How do I test this without an actual package name?
Also, I can't seem to be able to use:
Windows.System.Launcher.LaunchUriAsync(new Uri(appStoreURL));
I get:
Error:["'Windows' is undefined file:x-wmapp0:www\/js\/......
Any ideas?
SOLUTION:
Using Benoit's answer and some other stuff I found I managed to link straight to the review section by adding the following Plugin to my cordovalib:
LaunchReview.cs
using WPCordovaClassLib.Cordova.Commands;
using Microsoft.Phone.Tasks;
namespace Cordova.Extension.Commands
{
public class LaunchReview : BaseCommand
{
public void launchReview(string options)
{
// Use the Marketplace review task to launch the Store or Marketplace and then display the review page for the current app.
MarketplaceReviewTask marketplaceReviewTask = new MarketplaceReviewTask();
marketplaceReviewTask.Show();
}
}
}
Note sure what value you are using for appurl but here is something which should work:
Windows.System.Launcher.LaunchUriAsync(new Uri("zune:reviewapp"));
or you can use:
MarketplaceReviewTask marketplaceReviewTask = new MarketplaceReviewTask();
marketplaceReviewTask.Show();
To call it from javascript just create a plugin:
namespace Cordova.Extension.Commands
{
public class LaunchReview: BaseCommand
{
public void launchReview(string options)
{
// all JS callable plugin methods MUST have this signature!
// public, returning void, 1 argument that is a string
MarketplaceReviewTask marketplaceReviewTask = new MarketplaceReviewTask();
marketplaceReviewTask.Show();
}
}
}
that you can use it like this from javascript:
cordova.exec(win, fail, "LaunchReview", "launchReview", [""]);
Here is the link to the plugin dev guide for windows phone
If you want to use window.open then you will need to modify the PhoneGap source code to use LAunchUri because currently it's just using WebBrowserTask instead of LaunchUri. The function to modify is Plugin/InAppBrowser.cs>ShowSystemBrowser
I used InAppBrowser cordova plugin.
cordova plugin add org.apache.cordova.inappbrowser
To open wp8 store i call from javascript:
window.open(UrlToMyApp, '_blank', 'location=yes');

Issuing a synchronous HTTP GET request or invoking shell script in JavaScript from iOS UIAutomation

I am trying to use Apple's UIAutomation to write unit tests for an iOS Application that has a server-side component. In order to setup the test server in various states (as well as simulate two clients communicating through my server), I would like to issue HTTP get requests from within my javascript-based test.
Can anyone provide an example of how to either issue HTTP GET requests directly from within UIAutomation javascript tests, or how to invoke a shell script from within my UIAutomation javascript tests?
FWIW, most of the core objects made available by all browsers are missing within the UIAutomation runtime. Try to use XMLHTTPRequest for example and you will get an exception reporting that it cannot find the variable.
Thanks!
Folks,
I was able to work around this by sending HTTP requests to the iOS client to process and return the results in a UIAlertView. Note that all iOS code modifications are wrapped in #if DEBUG conditional compilation directives.
First, setup your client to send out notifications in the event of a device shake. Read this post for more information.
Next, in your iOS main app delegate add this code:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(deviceShakenShowDebug:)
name:#"DeviceShaken"
object:nil];
Then add a method that looks something like this:
- (void) deviceShakenShowDebug:(id)sender
{
if (!self.textFieldEnterDebugArgs)
{
self.textFieldEnterDebugArgs = [[[UITextField alloc] initWithFrame:CGRectMake(0, 0, 260.0, 25.0)] autorelease];
self.textFieldEnterDebugArgs.accessibilityLabel = #"AlertDebugArgsField";
self.textFieldEnterDebugArgs.isAccessibilityElement = YES;
[self.textFieldEnterDebugArgs setBackgroundColor:[UIColor whiteColor]];
[self.tabBarController.selectedViewController.view addSubview:self.textFieldEnterDebugArgs];
[self.tabBarController.selectedViewController.view bringSubviewToFront:self.textFieldEnterDebugArgs];
}
else
{
if ([self.textFieldEnterDebugArgs.text length] > 0)
{
if ([self.textFieldEnterDebugArgs.text hasPrefix:#"http://"])
{
[self doDebugHttpRequest:self.textFieldEnterDebugArgs.text];
}
}
}
}
- (void)requestDidFinishLoad:(TTURLRequest*)request
{
NSString *response = [[[NSString alloc] initWithData:((TTURLDataResponse*)request.response).data
encoding:NSUTF8StringEncoding] autorelease];
UIAlertView *resultAlert =
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Request Loaded",#"")
message:response
delegate:nil
cancelButtonTitle:NSLocalizedString(#"OK",#"")
otherButtonTitles:nil] autorelease];
resultAlert.accessibilityLabel = #"AlertDebugResult";
[resultAlert show];
}
This code will add a UITextField to the very top view controller after a shake, slapped right above any navigation bar or other UI element. UIAutomation, or you the user, can manually enter a URL into this UITextField. When you shake the device again, if the text begins with "http" it will issue an HTTP request in code (exercise for the reader to implement doDebugHttpRequest).
Then, in my UIAutomation JavaScript file, I have defined the following two functions:
function httpGet(url, delayInSec) {
if (!delayInSec) delay = 1;
var alertDebugResultSeen = false;
var httpResponseValue = null;
UIATarget.onAlert = function onAlert(alert) {
httpResponseValue = alert.staticTexts().toArray()[1].name();
alert.buttons()[0].tap();
alertDebugResultSeen = true;
}
var target = UIATarget.localTarget();
var application = target.frontMostApp();
target.shake(); // bring up the input field
application.mainWindow().textFields()["AlertDebugArgsField"].setValue(url);
target.shake(); // send back to be processed
target.delay(delayInSec);
assertTrue(alertDebugResultSeen);
return httpResponseValue;
}
function httpGetJSON(url, delayInSec) {
var response = httpGet(url, delayInSec);
return eval('(' + response + ')');
}
Now, in my javascript file, I can call
httpGet('http://localhost:3000/do_something')
and it will execute an HTTP request. If I want JSON data back from the server, I call
var jsonResponse = httpGetJSON('http://localhost:3000/do_something')
If I know it is going to be a long-running call, I call
var jsonResponse = httpGetJSON('http://localhost:3000/do_something', 10 /* timeout */)
I've been using this approach successfully now for several weeks.
Try performTaskWithPathArgumentsTimeout
UIATarget.host().performTaskWithPathArgumentsTimeout("/usr/bin/curl", "http://google.com", 30);
Just a small correction. The answer that suggests using UIATarget.host().performTaskWithPathArgumentsTimeout is an easy way to make a request on a URL in iOS 5.0+, but the syntax of the example is incorrect. Here is the correct way to make this call:
UIATarget.host().performTaskWithPathArgumentsTimeout("/usr/bin/curl", ["http://google.com"], 30);
The "[" around the "args" param is important, and the test will die with an exception similar to the following if you forget the brackets:
Error: -[__NSCFString count]: unrecognized selector sent to instance
Here is a fully working example that hits google.com and logs all the output:
var result = UIATarget.host().performTaskWithPathArgumentsTimeout("/usr/bin/curl", ["http://www.google.com"], 30);
UIALogger.logDebug("exitCode: " + result.exitCode);
UIALogger.logDebug("stdout: " + result.stdout);
UIALogger.logDebug("stderr: " + result.stderr);
+1 for creative use of "shake()". However, that's not an option for some projects, especially those that actually use the shake feature.
Think outside the box. Do the fetching with something else (Python, Ruby, node.js, bash+wget, etc). Then, you can use the pre-canned response and auto-generate the ui-test.js on the fly by including that dynamically generated json payload as the "sample data" into the test. Then you simply execute the test.
In my opinion, the test is the test, leave that alone. The test data you are using, if it's that dynamic, it ought to be separated from the test itself. By doing it this way of fetching / generating JSON, and referencing it from the test, you can update that JSON however often you like, either immediately right before every test, or on a set interval like when you know the server has been updated. I'm not sure you would want to generate it while the test is running, that seems like it would create problems. Taking it to the next level, you could get fancy and use functions that calculate what values ought to be based on other values, and expose them as "dynamic properties" of the data, rather than that math being inside the test, but at that point I think the discussion is more of an academic one rather than the practical one of how.
Apple has recently updated UIAutomation to include a new UIAHost element for performing a task on the Host that is running the instance of Instruments that is executing the tests.

Categories