NewGenApps Blog posts

Create Plugins using Phonegap

Written by Anurag | Dec 17, 2012 6:30:00 PM

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

This is in continuation to our PhoneGap tutorial series. This tutorial teaches you how to build the plugins using PhoneGap Framework.

If you are new then you can got to our previous tutorial in this series. 

 

 

 

 

 

 

PhoneGap provides several device APIs to access almost all sorts of things on a device. But when it comes to extend our requirements beyond such built-in-features or simply want to do something that is not already supported, there is a need aroused to write your own custom plugins. Just like adding a third-party plugin, we can write a little bit of native code and add our own plugin to PhoneGap as well.

In order to create such plugins, one has to be somewhat familiar with JavaScript as well as Objective-C/Java (in case of Phonegap app supporting iOS/Android environment).

Now moving on to create PhoneGap plugin for iOS development, this tutorial assumes that one is familiar with JavaScript, Objective-C, the iOS SDK, and the XCode development environment.

 

Writing a Plugin to save your Image to Photos Library, Documents Directory :

STEP 1. Create a new project using the Terminal as we have already discussed in our previous blog.

STEP 2. We need to create our initial page using HTML, CSS for how it will be presented for user interaction.

In index.html we will have an image (that is to be saved using our plugin), and two buttons : one for saving to photos library and another for saving to the documents directory. So we have it as :

 

  1. <!DOCTYPE html>

  2. <html>

  3. <head>

  4. <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no;" />

  5. <link href="css/index.css" type="text/css" rel="stylesheet" />

  6. <script src="cordova-2.2.0.js"></script>

  7. <script src="js/index.js"></script>

  8. </head>

  9. <body onload="appInitialize.initialize();">

  10. <div id="header">

  11. <div id="title">Custom Plugins</div>

  12. </div>

  13. <h3>Here is the image we are going to save</h2>

  14. <br>

  15. <img id="imageToSave" src="img/flower.png">

  16. <button onclick="appInitialize.saveToLibrary();">Save To Library</button>

  17. <button onclick="appInitialize.saveToDocument();">Save To Document</button>

  18. </body>

  19. </html>

In line no. 5, we include index.css to style our page elements.

In line no. 6, we include cordova-2.2.0.js to use the functions of phonegap.

In line no. 7, we include index.js to call the methods when the user clicks the buttons.

In line no. 9, a method defined in index.js, appInitialize.initialize() is called to check if the device is ready to use.

In line no. 16, 17, we call the methods to be invoked when the buttons are clicked. These methods too are defined in our index.js file.

 

STEP 3. So for now we have our index.js as :

 

  1. var appInitialize = {

  2. initialize: function() {

  3. document.addEventListener("deviceready", appInitialize.onDeviceReady, false);

  4. },

  5. onDeviceReady: function () {

  6. console.log('Ready to use');

  7. //navigator.notification.alert("Success : \r\n");

  8. },

  9. saveToLibrary: function () {

  10. console.log('Ready to use saveToLibrary');

  11. },

  12. saveToDocument: function () {

  13. console.log('Ready to use saveToDocument');

  14. },

  15. };

 

When onDeviceReady is called from initialize method, a log is printed on the console. You can also show an alert using the notification API of Phonegap(see line no. 9 which is currently commented out).

 

The two methods are invoked on button clicks : saveToLibrary, saveToDocument and print the log on the console respectively. This is just to ensure that our methods are being called.

 

STEP 4. Now we need to create another js object file say, MyCustomSavePlugin.js.

STEP 5. Create a new Objective-c class say, MyCustomSavePluginCommand.h, MyCustomSavePluginCommand.m 

 

Writing code in MyCustomSavePlugin.js

 

  1. MyCustomSavePlugin.install = function() {

  2. if(!window.plugins) {

  3. window.plugins = {};

  4. }

  5. window.plugins.myCustomSavePlugin = new MyCustomSavePlugin();

  6. return window.plugins.myCustomSavePlugin;

  7. };

  8. function MyCustomSavePlugin() {

  9. // Does nothing

  10. };

  11. function mymethod(imgId) {

  12. var image = document.getElementById(imgId);

  13. // Create an empty canvas element with the same dimensions as that of the image to be saved

  14. var canvas = document.createElement("canvas");

  15. canvas.width = image.width;

  16. canvas.height = image.height;

  17. // Copy the image contents to the canvas

  18. var ctx = canvas.getContext("2d");

  19. ctx.drawImage(image, 0, 0);

  20. // Get the data-URL formatted image from the canvas

  21. var dataURL = canvas.toDataURL().replace(/data:image\/png;base64,/,'');

  22. return [dataURL, image.width, image.height];

  23. };

  24. MyCustomSavePlugin.prototype.saveToLibrary = function(successCallback, failureCallback, imgId) {

  25. // successCallback required

  26. if (typeof successCallback != "function") {

  27. console.log("MyCustomSavePlugin Error: successCallback is not a function");

  28. return;

  29. }

  30. if (typeof failureCallback != "function") {

  31. console.log("MyCustomSavePlugin Error: failureCallback is not a function");

  32. return;

  33. }

  34. var data = mymethod(imgId);

  35. cordova.exec(successCallback, failureCallback, "MyCustomSavePluginCommand","saveToLibraryMethod",[data[0], data[1], data[2]]);

  36. };

  37. MyCustomSavePlugin.prototype.saveToDocuments = function(successCallback, failureCallback, imgId) {

  38. // successCallback required

  39. if (typeof successCallback != "function") {

  40. console.log("MyCustomSavePlugin Error: successCallback is not a function");

  41. return;

  42. }

  43. if (typeof failureCallback != "function") {

  44. console.log("MyCustomSavePlugin Error: failureCallback is not a function");

  45. return;

  46. }

  47. var data = mymethod(imgId);

  48. cordova.exec(successCallback, failureCallback, "MyCustomSavePluginCommand","saveToDocumentsMethod",[data[0]]);

  49. };

 

 

In line no. 1, Add a class method to the object to “install” the object at runtime which will instantiate the object and make it accessible via window.plugins.

In line no. 9, Create a new JavaScript Object class.

In line no. 13, Get the data-URL formatted image (from img tag in index.html) using the canvas.

In line no. 27, Method call to save image to library.

In line no. 41, Method call to save image to document directory.

 

Now our native Objective-c class for this plugin will look as follows :

MyCustomSavePluginCommand.h

 

#import <Cordova/CDVPlugin.h>

 

@interface MyCustomSavePluginCommand : CDVPlugin

@end

 

MyCustomSavePluginCommand.m

 

#import "MyCustomSavePluginCommand.h"

#import <Cordova/NSData+Base64.h>

 

@implementation MyCustomSavePluginCommand

 

- (void)saveToLibraryMethod:(CDVInvokedUrlCommand*)command{

CDVPluginResult* pluginResult = nil;

 

NSString *myarg = [command.arguments objectAtIndex:0];

NSString *myarg1 = [command.arguments objectAtIndex:1];

NSString *myarg2 = [command.arguments objectAtIndex:2];

 

int width = [myarg1 integerValue];

int height = [myarg2 integerValue];

 

if (myarg != nil) {

pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];

} else {

pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Arg was null"];

}

 

NSData* imageData = [NSData dataFromBase64String:myarg];

UIImage* image = [[UIImage alloc] initWithData:imageData];

 

CGRect rect = CGRectMake(0, 0, width, height);

UIGraphicsBeginImageContext(rect.size);

[image drawInRect:rect];

UIImage *result = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

 

UIImageWriteToSavedPhotosAlbum(result, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);

 

[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];

}

 

- (void)saveToDocumentsMethod:(CDVInvokedUrlCommand*)command

{

CDVPluginResult* pluginResult = nil;

NSString* myarg = [command.arguments objectAtIndex:0];

 

if (myarg != nil) {

pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];

} else {

pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Arg was null"];

}

 

NSArray *paths = NSSearchPathForDirectoriesInDomains

(NSDocumentDirectory, NSUserDomainMask, YES);

NSString *documentsDirectory = [paths objectAtIndex:0];

 

//make a file name to write the data to using the documents directory:

NSString *fileName = [NSString stringWithFormat:@"%@/image.png",

documentsDirectory];

 

NSData* imageData = [NSData dataFromBase64String:myarg];

 

BOOL saved = [imageData writeToFile:fileName atomically:YES]; //Write the file

 

if (saved) {

UIAlertView* alert = [[UIAlertView alloc]initWithTitle:@"Saved" message:@"Image saved successfully." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];

[alert show];

[alert release];

}

 

else {

UIAlertView* alert = [[UIAlertView alloc]initWithTitle:@"Error" message:@"Image could not be saved." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];

[alert show];

[alert release];

}

 

[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];

}

 

- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo

{

CDVPluginResult* pluginResult = nil;

 

if (error != NULL) // Was there an error?

{

// Show error message...

NSLog(@"ERROR: %@",error);

pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Arg was null"];

}

 

else // No errors

{

// Show message image successfully saved

NSLog(@"IMAGE SAVED!");

 

pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];

 

UIAlertView* alert = [[UIAlertView alloc]initWithTitle:@"Saved" message:@"Image saved successfully." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];

[alert show];

[alert release];

 

}

}

 

@end

 

STEP 6. In index.js file, we have our plugin to be automatically “installed” when device is ready. For this, we have to call a method in onDeviceReady function ,

 

cordova.addConstructor(MyCustomSavePlugin.install);

 

STEP 7. And to call the plugin methods, we have to call those methods defined in MyCustomSavePlugin.js

 

Now index.js would finally look as :

 

  1. var appInitialize = {

  2. initialize: function() {

  3. document.addEventListener("deviceready", appInitialize.onDeviceReady, false);

  4. },

  5. onDeviceReady: function () {

  6. console.log('Ready to use');

  7. //navigator.notification.alert("Success : \r\n");

  8. cordova.addConstructor(MyCustomSavePlugin.install);

  9. },

  10. saveToLibrary: function () {

  11. console.log('Ready to use saveToLibrary');

  12. var myCustomSavePlugin = window.plugins.myCustomSavePlugin;

  13. myCustomSavePlugin.saveToLibrary(

  14. function(msg){

  15. //alert('msg');

  16. },

  17. function(err){

  18. //alert('error');

  19. },

  20. 'imageToSave'

  21. );

  22. },

  23. saveToDocument: function () {

  24. console.log('Ready to use saveToDocument');

  25. var myCustomSavePlugin = window.plugins.myCustomSavePlugin;

  26. myCustomSavePlugin.saveToDocuments(function(msg){

  27. //alert('msg');

  28. },

  29. function(err){

  30. //alert('error');

  31. },

  32. 'imageToSave'

  33. );

  34. },

  35. };

 

 

STEP 8. In cordova.plist file in your Resources, add an entry in the Plugins key

 

Key: MyCustomSavePluginCommand

Value: MyCustomSavePluginCommand

 

This step is very essential for adding any customized plugin to your Phonegap project otherwise cordova will not recognize our plugin.

 

NOTE : The plugin must be added to Plugins key (a Dictionary) of the Cordova.plist file in your Cordova-iOS application's project folder.

 

<key>service_name</key>

<string>PluginClassName</string>

 

The key service_name should match what you use in the JavaScript exec call, and the value will be the name of the Objective-C class of the plugin. Without this added, the plugin may compile but will not be reachable by Cordova.

 

 

 

STEP 9. Run the app. We have created a simple custom Phonegap plugin for iOS.

 

NOTE : This tutorial has been developed by using Phonegap 2.2.0 version. For more information regarding this you can visit the official site of Phonegap.