Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'master' of github.com:phonegap/phonegap-plugins

  • Loading branch information...
commit 6068c7af3414059e25fac63f0624a210cf3ea350 2 parents 500866c + d3b4a0a
Jesse authored
Showing with 1,990 additions and 68 deletions.
  1. +117 −61 Android/BarcodeScanner/BarcodeScanner.java
  2. +25 −1 Android/BarcodeScanner/README.md
  3. +41 −0 Android/BarcodeScanner/barcodescanner.js
  4. +79 −0 Android/ContactView/ContactView.java
  5. +23 −0 Android/ContactView/ContactView.js
  6. +36 −0 Android/ContactView/README.txt
  7. +245 −0 Android/FileUploader/FileUploader.java
  8. +70 −0 Android/FileUploader/README.md
  9. +68 −0 Android/FileUploader/fileuploader.js
  10. +1 −1  iPhone/Badge/Badge.js
  11. +24 −0 iPhone/BarcodeScanner/BarcodeScanner.h
  12. +51 −0 iPhone/BarcodeScanner/BarcodeScanner.mm
  13. +53 −0 iPhone/BarcodeScanner/README.md
  14. +32 −0 iPhone/BarcodeScanner/barcodescanner.js
  15. +34 −0 iPhone/FileUploader/FileUploader.h
  16. +223 −0 iPhone/FileUploader/FileUploader.m
  17. +81 −0 iPhone/FileUploader/README.md
  18. +76 −0 iPhone/FileUploader/fileuploader.js
  19. +38 −0 iPhone/InAppPurchaseManager/InAppPurchaseManager.h
  20. +153 −0 iPhone/InAppPurchaseManager/InAppPurchaseManager.js
  21. +150 −0 iPhone/InAppPurchaseManager/InAppPurchaseManager.m
  22. +97 −0 iPhone/InAppPurchaseManager/README.md
  23. +5 −5 iPhone/MapKitPlug/src/MapKit.m
  24. +50 −0 iPhone/SMSComposer/README.md
  25. +13 −0 iPhone/SMSComposer/SMSComposer.h
  26. +51 −0 iPhone/SMSComposer/SMSComposer.js
  27. +85 −0 iPhone/SMSComposer/SMSComposer.m
  28. +68 −0 iPhone/SMSComposer/index.html
  29. +1 −0  iPhone/ScreenShot/ScreenShot.h
View
178 Android/BarcodeScanner/BarcodeScanner.java 100644 → 100755
@@ -11,34 +11,31 @@
import org.json.JSONArray;
import org.json.JSONException;
-import com.phonegap.DroidGap;
-import com.phonegap.api.PhonegapActivity;
-import com.phonegap.api.Plugin;
-import com.phonegap.api.PluginResult;
-
import android.app.Activity;
import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
-
-import android.content.ActivityNotFoundException;
-
import android.net.Uri;
+import com.phonegap.api.PhonegapActivity;
+import com.phonegap.api.Plugin;
+import com.phonegap.api.PluginResult;
+
/**
* This calls out to the ZXing barcode reader and returns the result.
*/
public class BarcodeScanner extends Plugin {
public static final int REQUEST_CODE = 0x0ba7c0de;
-
+
public static final String defaultInstallTitle = "Install Barcode Scanner?";
public static final String defaultInstallMessage = "This requires the free Barcode Scanner app. Would you like to install it now?";
public static final String defaultYesString = "Yes";
public static final String defaultNoString = "No";
-
+
public String callback;
-
+
/**
* Constructor.
*/
@@ -47,7 +44,7 @@ public BarcodeScanner() {
/**
* Executes the request and returns PluginResult.
- *
+ *
* @param action The action to execute.
* @param args JSONArray of arguments for the plugin.
* @param callbackId The callback id used when calling back into JavaScript.
@@ -55,14 +52,51 @@ public BarcodeScanner() {
*/
public PluginResult execute(String action, JSONArray args, String callbackId) {
this.callback = callbackId;
-
+
try {
- if (action.equals("scan")) {
+ if (action.equals("encode")) {
+ String type = null;
+ if(args.length() > 0) {
+ type = args.getString(0);
+ }
+
+ String data = null;
+ if(args.length() > 1) {
+ data = args.getString(1);
+ }
+
+ String installTitle = defaultInstallTitle;
+ if(args.length() > 2) {
+ installTitle = args.getString(2);
+ }
+
+ String installMessage = defaultInstallMessage;
+ if(args.length() > 3) {
+ installMessage = args.getString(3);
+ }
+
+ String yesString = defaultYesString;
+ if(args.length() > 4) {
+ yesString = args.getString(4);
+ }
+
+ String noString = defaultNoString;
+ if(args.length() > 5) {
+ noString = args.getString(5);
+ }
+
+ // if data.TypeOf() == Bundle, then call
+ // encode(type, Bundle)
+ // else
+ // encode(type, String)
+ this.encode(type, data, installTitle, installMessage, yesString, noString);
+ }
+ else if (action.equals("scan")) {
String barcodeTypes = null;
if(args.length() > 0) {
barcodeTypes = args.getString(0);
}
-
+
String installTitle = defaultInstallTitle;
if(args.length() > 1) {
installTitle = args.getString(1);
@@ -94,11 +128,11 @@ public PluginResult execute(String action, JSONArray args, String callbackId) {
e.printStackTrace();
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
}
- }
-
-
+ }
+
+
/**
- * Initiates a barcode scan. If the ZXing scanner isn't installed, the user
+ * Initiates a barcode scan. If the ZXing scanner isn't installed, the user
* will be prompted to install it.
* @param types The barcode types to accept
* @param installTitle The title for the dialog box that prompts the user to install the scanner
@@ -112,21 +146,21 @@ public void scan(String barcodeFormats, String installTitle, String installMessa
// A null format means we scan for any type
if (barcodeFormats != null) {
- // Tell the scanner what types we're after
- intentScan.putExtra("SCAN_FORMATS", barcodeFormats);
+ // Tell the scanner what types we're after
+ intentScan.putExtra("SCAN_FORMATS", barcodeFormats);
}
- try {
- this.ctx.startActivityForResult((Plugin) this, intentScan, REQUEST_CODE);
- } catch (ActivityNotFoundException e) {
+ try {
+ this.ctx.startActivityForResult((Plugin) this, intentScan, REQUEST_CODE);
+ } catch (ActivityNotFoundException e) {
showDownloadDialog(installTitle, installMessage, yesString, noString);
}
}
/**
- * Called when the barcode scanner exits
- *
- * @param requestCode The request code originally supplied to startActivityForResult(),
+ * Called when the barcode scanner exits
+ *
+ * @param requestCode The request code originally supplied to startActivityForResult(),
* allowing you to identify who this result came from.
* @param resultCode The integer result code returned by the child activity through its setResult().
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
@@ -139,44 +173,66 @@ public void onActivityResult(int requestCode, int resultCode, Intent intent) {
} else {
this.error(new PluginResult(PluginResult.Status.ERROR), this.callback);
}
- }
+ }
}
-
- private void showDownloadDialog(final String title, final String message, final String yesString, final String noString) {
+
+ private void showDownloadDialog(final String title, final String message, final String yesString, final String noString) {
final PhonegapActivity context = this.ctx;
Runnable runnable = new Runnable() {
- public void run() {
-
- AlertDialog.Builder dialog = new AlertDialog.Builder(context);
- dialog.setTitle(title);
- dialog.setMessage(message);
- dialog.setPositiveButton(yesString, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dlg, int i) {
- dlg.dismiss();
- Intent intent = new Intent(Intent.ACTION_VIEW,
- Uri.parse("market://search?q=pname:com.google.zxing.client.android")
- );
- try {
- context.startActivity(intent);
- } catch (ActivityNotFoundException e) {
-// We don't have the market app installed, so download it directly.
- Intent in = new Intent(Intent.ACTION_VIEW);
- in.setData(Uri.parse("http://zxing.googlecode.com/files/BarcodeScanner3.5.apk"));
- context.startActivity(in);
-
- }
+ public void run() {
+
+ AlertDialog.Builder dialog = new AlertDialog.Builder(context);
+ dialog.setTitle(title);
+ dialog.setMessage(message);
+ dialog.setPositiveButton(yesString, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dlg, int i) {
+ dlg.dismiss();
+ Intent intent = new Intent(Intent.ACTION_VIEW,
+ Uri.parse("market://search?q=pname:com.google.zxing.client.android")
+ );
+ try {
+ context.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ // We don't have the market app installed, so download it directly.
+ Intent in = new Intent(Intent.ACTION_VIEW);
+ in.setData(Uri.parse("http://zxing.googlecode.com/files/BarcodeScanner3.53.apk"));
+ context.startActivity(in);
}
- });
- dialog.setNegativeButton(noString, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dlg, int i) {
- dlg.dismiss();
- }
- });
- dialog.create();
- dialog.show();
- }
- };
- context.runOnUiThread(runnable);
+
+ }
+ });
+ dialog.setNegativeButton(noString, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dlg, int i) {
+ dlg.dismiss();
+ }
+ });
+ dialog.create();
+ dialog.show();
+ }
+ };
+ context.runOnUiThread(runnable);
+ }
+
+ /**
+ * Initiates a barcode encode. If the ZXing scanner isn't installed, the user
+ * will be prompted to install it.
+ * @param type The barcode type to encode
+ * @param data The data to encode in the bar code
+ * @param installTitle The title for the dialog box that prompts the user to install the scanner
+ * @param installMessage The message prompting the user to install the barcode scanner
+ * @param yesString The string "Yes" or localised equivalent
+ * @param noString The string "No" or localised version
+ */
+ public void encode(String type, String data, String installTitle, String installMessage, String yesString, String noString) {
+ Intent intentEncode = new Intent("com.google.zxing.client.android.ENCODE");
+ intentEncode.putExtra("ENCODE_TYPE", type);
+ intentEncode.putExtra("ENCODE_DATA", data);
+
+ try {
+ this.ctx.startActivity(intentEncode);
+ } catch (ActivityNotFoundException e) {
+ showDownloadDialog(installTitle, installMessage, yesString, noString);
}
+ }
}
View
26 Android/BarcodeScanner/README.md
@@ -7,7 +7,12 @@ If the user doesn't have the app install they will be promped to install it the
1. To install the plugin, move barcodescanner.js to your project's www folder and include a reference to it
in your html files.
-2. Create a folder called "beetight" within your project's src/com/ folder and move the java file into it.
+2. Create a folder called 'src/com/beetight/barcodescanner' within your project's src/com/ folder.
+3. And copy the java file into that new folder.
+
+`mkdir <your_project>/src/com/beetight/barcodescanner`
+
+`cp ./BarcodeScanner.java <your_project>/src/com/beetight/barcodescanner`
## Using the plugin ##
The plugin creates the object `window.plugins.barcodeScanner` with one method `scan(types, success, fail, options)`
@@ -50,6 +55,25 @@ A full example could be:
alert("Scanning failed: " + error);
}, {yesString: "Install"}
);
+
+## Encoding a Barcode ##
+Supported encoding types:
+
+* TEXT_TYPE
+* EMAIL_TYPE
+* PHONE_TYPE
+* SMS_TYPE
+
+
+A full example could be:
+
+ window.plugins.barcodeScanner.encode(BarcodeScanner.Encode.TEXT_TYPE, "http://www.nytimes.com", function(success) {
+ alert("encode success: " + success);
+ }, function(fail) {
+ alert("encoding failed: " + fail);
+ }, {yesString: "Install"}
+ );
+
## BUGS AND CONTRIBUTIONS ##
The latest bleeding-edge version is available [on GitHub](http://github.com/ascorbic/phonegap-plugins/tree/master/Android/)
View
41 Android/BarcodeScanner/barcodescanner.js 100644 → 100755
@@ -28,6 +28,15 @@ BarcodeScanner.Type = {
ALL_CODE_TYPES: null
}
+BarcodeScanner.Encode = {
+ TEXT_TYPE: "TEXT_TYPE",
+ EMAIL_TYPE: "EMAIL_TYPE",
+ PHONE_TYPE: "PHONE_TYPE",
+ SMS_TYPE: "SMS_TYPE",
+ // CONTACT_TYPE: "CONTACT_TYPE", // TODO: not implemented, requires passing a Bundle class from Javascriopt to Java
+ // LOCATION_TYPE: "LOCATION_TYPE" // TODO: not implemented, requires passing a Bundle class from Javascriopt to Java
+ }
+
BarcodeScanner.prototype.scan = function(types, success, fail, options) {
/* These are the strings used in the dialog that appears if ZXing isn't installed */
@@ -61,6 +70,38 @@ BarcodeScanner.prototype.scan = function(types, success, fail, options) {
}, 'BarcodeScanner', 'scan', [types, installTitle, installMessage, yesString, noString]);
};
+BarcodeScanner.prototype.encode = function(type, data, success, fail, options) {
+
+ /* These are the strings used in the dialog that appears if ZXing isn't installed */
+ var installTitle = "Install Barcode Scanner?";
+ var installMessage = "This requires the free Barcode Scanner app. Would you like to install it now?";
+ var yesString = "Yes";
+ var noString = "No";
+ if (typeof options != 'undefined') {
+ if(typeof options.installTitle != 'undefined') {
+ installTitle = options.installTitle;
+ }
+
+ if(typeof options.installMessage != 'undefined') {
+ installMessage = options.installMessage;
+ }
+
+ if(typeof options.yesString != 'undefined') {
+ yesString = options.yesString;
+ }
+
+ if(typeof options.noString != 'undefined') {
+ noString = options.noString;
+ }
+ }
+
+ return PhoneGap.exec(function(args) {
+ success(args);
+ }, function(args) {
+ fail(args);
+ }, 'BarcodeScanner', 'encode', [type, data, installTitle, installMessage, yesString, noString]);
+};
+
PhoneGap.addConstructor(function() {
PhoneGap.addPlugin('barcodeScanner', new BarcodeScanner());
PluginManager.addService("BarcodeScanner","com.beetight.barcodescanner.BarcodeScanner");
View
79 Android/ContactView/ContactView.java
@@ -0,0 +1,79 @@
+package com.rearden;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+
+import com.phonegap.api.Plugin;
+import com.phonegap.api.PluginResult;
+
+public class ContactView extends Plugin {
+ private static final int PICK_CONTACT = 1;
+ private String callback;
+
+ @Override
+ public PluginResult execute(String action, JSONArray args, String callbackId) {
+ startContactActivity();
+ PluginResult mPlugin = new PluginResult(PluginResult.Status.NO_RESULT);
+ mPlugin.setKeepCallback(true);
+ this.callback = callbackId;
+ return mPlugin;
+ }
+
+ public void startContactActivity() {
+ Intent intent = new Intent(Intent.ACTION_PICK);
+ intent.setType(ContactsContract.Contacts.CONTENT_TYPE);
+ this.ctx.startActivityForResult((Plugin) this, intent, PICK_CONTACT);
+ }
+
+ @Override
+ public void onActivityResult(int reqCode, int resultCode, Intent data) {
+ String name = null;
+ String number = null;
+ switch (reqCode) {
+ case (PICK_CONTACT):
+ if (resultCode == Activity.RESULT_OK) {
+ Uri contactData = data.getData();
+ Cursor c = this.ctx.managedQuery(contactData, null, null, null, null);
+ if (c.moveToFirst()) {
+ String ContactID = c.getString(c
+ .getColumnIndex(ContactsContract.Contacts._ID));
+ String hasPhone = c.getString(c.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));
+
+ if (Integer.parseInt(hasPhone) == 1) {
+ Cursor phoneCursor = this.ctx.managedQuery(
+ ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
+ null,
+ ContactsContract.CommonDataKinds.Phone.CONTACT_ID
+ + "='" + ContactID + "'", null,
+ null);
+ while (phoneCursor.moveToNext()) {
+ number = phoneCursor
+ .getString(phoneCursor
+ .getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
+ }
+ }
+
+ name = c.getString(c.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME));
+ JSONObject contactObject = new JSONObject();
+ try {
+ contactObject.put("name", name);
+ contactObject.put("phone", number);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+
+ this.success(new PluginResult(PluginResult.Status.OK,
+ contactObject), this.callback);
+ }
+ }
+ break;
+ }
+ }
+}
View
23 Android/ContactView/ContactView.js
@@ -0,0 +1,23 @@
+var ContactView = function() {};
+
+ContactView.prototype.show = function(successCallback, failCallback) {
+
+ function success(args) {
+ successCallback(args);
+ }
+
+ function fail(args) {
+ failCallback(args);
+ }
+
+ return PhoneGap.exec(function(args) {
+ success(args);
+ }, function(args) {
+ fail(args);
+ }, 'ContactView', '', []);
+};
+
+PhoneGap.addConstructor(function() {
+ PhoneGap.addPlugin('contactView', new ContactView());
+ PluginManager.addService("ContactView","com.rearden.ContactView");
+});
View
36 Android/ContactView/README.txt
@@ -0,0 +1,36 @@
+This plugin is a different philosophy to how contacts can work with PhoneGap Android. Rather than pulling in the contacts into HTML, this plugin allows you to leave the PhoneGap webview and enter into a native contact picker. Once the user has selected a contact, they will be sent back into the PhoneGap webview with the name of the contact available.
+
+
+How to use:
+
+Usage:
+
+window.plugins.contactView.show(success, failure);
+
+Success returns an object with the name and the phone of the contact selected.
+
+Example:
+
+ document.querySelector("#contact-name-to-native").addEventListener("touchstart", function() {
+ window.plugins.contactView.show(
+ function(contact) {
+ document.getElementById("contact-name-from-native").value = contact.name;
+ document.getElementById("contact-phone").value = contact.phone;
+ },
+ function(fail) {
+ alert("We were unable to get the contact you selected.");
+ }
+ );
+ }, false);
+
+
+
+For the current files to work, you'll need to create a package (folders) called com.rearden. You can change this to whatever you like, just update the ContactView.js and ContactView.java.
+
+ContactView.js should go in the asset folder and should be referenced in your index.html file.
+
+
+Limitations:
+
+It only grabs the Name and Phone number, but not any other information.
+It only works with Android API 2.0 and above. Future versions will include older APIs.
View
245 Android/FileUploader/FileUploader.java
@@ -0,0 +1,245 @@
+/**
+*
+*/
+package com.beetight;
+
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import android.net.Uri;
+import java.net.URL;
+import java.util.Iterator;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.util.Log;
+import android.webkit.CookieManager;
+
+import com.phonegap.api.Plugin;
+import com.phonegap.api.PluginResult;
+
+/**
+* @author matt
+*
+*/
+public class FileUploader extends Plugin {
+
+ /* (non-Javadoc)
+ * @see com.phonegap.api.Plugin#execute(java.lang.String, org.json.JSONArray, java.lang.String)
+ */
+ @Override
+ public PluginResult execute(String action, JSONArray args, String callbackId) {
+
+ try {
+ String server = args.getString(0);
+ String file = args.getString(1);
+ JSONObject params = args.getJSONObject(2);
+
+ String fileKey = "file";
+ String fileName = "image.jpg";
+ String mimeType = "image/jpeg";
+ if(args.length() > 3) {
+ fileKey = args.getString(3);
+ }
+ if(args.length() > 4) {
+ fileName = args.getString(4);
+ }
+ if(args.length() > 5) {
+ mimeType = args.getString(5);
+ }
+
+ if (action.equals("upload")) {
+ upload(file, server, params, fileKey, fileName, mimeType, callbackId);
+ } else if (action.equals("uploadByUri")) {
+ Uri uri = Uri.parse(file);
+ upload(uri, server, params, fileKey, fileName, mimeType, callbackId);
+ } else {
+ return new PluginResult(PluginResult.Status.INVALID_ACTION);
+ }
+ PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
+ r.setKeepCallback(true);
+ return r;
+ } catch (JSONException e) {
+ e.printStackTrace();
+ return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
+ }
+
+ }
+
+ public void upload(Uri uri, String server, JSONObject params, final String fileKey, final String fileName, final String mimeType, String callbackId) {
+ try {
+ InputStream fileInputStream=this.ctx.getContentResolver().openInputStream(uri);
+ upload(fileInputStream, server, params, fileKey, fileName, mimeType, callbackId);
+ } catch (FileNotFoundException e) {
+ Log.e("PhoneGapLog", "error: " + e.getMessage(), e);
+ }
+ }
+
+ public void upload(String filename, String server, JSONObject params, final String fileKey, final String fileName, final String mimeType, String callbackId) {
+ File uploadFile = new File(filename);
+ try {
+ FileInputStream fileInputStream = new FileInputStream(uploadFile);
+ upload(fileInputStream, server, params, fileKey, fileName, mimeType, callbackId);
+ } catch (FileNotFoundException e) {
+ Log.e("PhoneGapLog", "error: " + e.getMessage(), e);
+ }
+
+ }
+
+
+ public void upload(InputStream fileInputStream, String server, JSONObject params, final String fileKey, final String fileName, final String mimeType, final String callbackId) {
+ try {
+
+ String lineEnd = "\r\n";
+ String td = "--";
+ String boundary = "*****com.beetight.formBoundary";
+
+ URL url = new URL(server);
+ HttpURLConnection conn = (HttpURLConnection)url.openConnection();
+
+// Get cookies that have been set in our webview
+ CookieManager cm = CookieManager.getInstance();
+ String cookie = cm.getCookie(server);
+
+ // allow inputs
+ conn.setDoInput(true);
+ // allow outputs
+ conn.setDoOutput(true);
+ // don't use a cached copy
+ conn.setUseCaches(false);
+ // use a post method
+ conn.setRequestMethod("POST");
+ // set post headers
+ conn.setRequestProperty("Connection","Keep-Alive");
+ conn.setRequestProperty("Content-Type","multipart/form-data;boundary="+boundary);
+ conn.setRequestProperty("Cookie", cookie);
+ // open data output stream
+ DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
+
+ try {
+ for (Iterator iter = params.keys(); iter.hasNext();) {
+ Object key = iter.next();
+ dos.writeBytes(td + boundary + lineEnd);
+ dos.writeBytes("Content-Disposition: form-data; name=\"" + key + "\"; ");
+ dos.writeBytes(lineEnd + lineEnd);
+ dos.writeBytes(params.getString(key.toString()));
+ dos.writeBytes(lineEnd);
+ }
+ } catch (JSONException e) {
+ Log.e("PhoneGapLog", "error: " + e.getMessage(), e);
+ }
+
+
+ dos.writeBytes(td + boundary + lineEnd);
+ dos.writeBytes("Content-Disposition: form-data; name=\"" + fileKey + "\";filename=\"" + fileName + "\"" + lineEnd);
+ dos.writeBytes("Content-Type: " + mimeType + lineEnd);
+
+ dos.writeBytes(lineEnd);
+ // create a buffer of maximum size
+ int bytesAvailable = fileInputStream.available();
+ final int total = bytesAvailable;
+ Log.e("PhoneGapLog", "available: " + bytesAvailable);
+
+ int maxBufferSize = 1024;
+ int bufferSize = Math.min(bytesAvailable, maxBufferSize);
+ byte[] buffer = new byte[bufferSize];
+ // read file and write it into form...
+ int bytesRead = fileInputStream.read(buffer, 0, bufferSize);
+ int progress = bytesRead;
+ int send = 0;
+ while (bytesRead > 0)
+ {
+ dos.write(buffer, 0, bufferSize);
+ bytesAvailable = fileInputStream.available();
+ bufferSize = Math.min(bytesAvailable, maxBufferSize);
+ bytesRead = fileInputStream.read(buffer, 0, bufferSize);
+ progress += bytesRead;
+ final int prog = progress;
+ Log.e("PhoneGapLog", "read " + progress + " of " + total);
+
+// Sending every progress event is overkill
+ if (send++ % 20 == 0) {
+ ctx.runOnUiThread(new Runnable () {
+ public void run() {
+ try {
+ JSONObject result = new JSONObject();
+ result.put("status", FileUploader.Status.PROGRESS);
+ result.put("progress", prog);
+ result.put("total", total);
+ PluginResult progressResult = new PluginResult(PluginResult.Status.OK, result);
+ progressResult.setKeepCallback(true);
+ success(progressResult, callbackId);
+ } catch (JSONException e) {
+ Log.e("PhoneGapLog", "error: " + e.getMessage(), e);
+ }
+ }
+ });
+// Give a chance for the progress to be sent to javascript
+ Thread.sleep(100);
+ }
+ }
+ // send multipart form data necessary after file data...
+ dos.writeBytes(lineEnd);
+ dos.writeBytes(td + boundary + td + lineEnd);
+
+ // close streams
+ fileInputStream.close();
+ dos.flush();
+ InputStream is = conn.getInputStream();
+ int ch;
+ StringBuffer b =new StringBuffer();
+ while( ( ch = is.read() ) != -1 ) {
+ b.append( (char)ch );
+ }
+ String s=b.toString();
+ dos.close();
+ JSONObject result = new JSONObject();
+ result.put("status", FileUploader.Status.COMPLETE);
+
+ result.put("progress", progress);
+ result.put("total", total);
+ result.put("result", s);
+ PluginResult progressResult = new PluginResult(PluginResult.Status.OK, result);
+ progressResult.setKeepCallback(true);
+ success(progressResult, callbackId);
+
+ }
+ catch (MalformedURLException e) {
+ Log.e("PhoneGapLog", "error: " + e.getMessage(), e);
+ PluginResult result = new PluginResult(PluginResult.Status.MALFORMED_URL_EXCEPTION, e.getMessage());
+ error(result, callbackId);
+ }
+ catch (FileNotFoundException e) {
+ Log.e("PhoneGapLog", "error: " + e.getMessage(), e);
+ PluginResult result = new PluginResult(PluginResult.Status.ERROR, e.getMessage());
+ error(result, callbackId);
+ }
+ catch (IOException e) {
+ Log.e("PhoneGapLog", "error: " + e.getMessage(), e);
+ PluginResult result = new PluginResult(PluginResult.Status.IO_EXCEPTION, e.getMessage());
+ error(result, callbackId);
+ } catch (InterruptedException e) {
+ Log.e("PhoneGapLog", "error: " + e.getMessage(), e);
+ PluginResult result = new PluginResult(PluginResult.Status.ERROR, e.getMessage());
+ error(result, callbackId);
+ } catch (JSONException e) {
+ Log.e("PhoneGapLog", "error: " + e.getMessage(), e);
+ PluginResult result = new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage());
+ error(result, callbackId);
+ }
+ }
+
+ public enum Status {
+ PROGRESS,
+ COMPLETE
+ }
+
+
+}
View
70 Android/FileUploader/README.md
@@ -0,0 +1,70 @@
+# File Uploader plugin for Phonegap #
+By Matt Kane
+
+## Adding the Plugin to your project ##
+
+1. To install the plugin, move fileuploader.js to your project's www folder and include a reference to it
+in your html files.
+2. Create a folder called "beetight" within your project's src/com/ folder and move the java file into it.
+
+## Using the plugin ##
+The plugin creates the object `window.plugins.fileUploader` with two methods, `upload` and `uploadByUri`.
+These are identical except for the format of the reference to the file to upload. `upload` takes an
+absolute path, e.g. `/sdcard/media/images/image.jpg`, while `uploadByUri` takes a content:// Uri,
+e.g. `content://media/external/images/media/5`.
+The full params are as follows:
+
+* server URL of the server that will receive the file
+* file Path or uri of the file to upload
+* fileKey Object with key: value params to send to the server
+* params Parameter name of the file
+* fileName Filename to send to the server. Defaults to image.jpg
+* mimeType Mimetype of the uploaded file. Defaults to image/jpeg
+* callback Success callback. Also receives progress messages during upload.
+* fail Error callback
+
+Note that the success callback isn't just called when the upload is complete, but also gets progress
+events while the file is uploaded. It's passed an object of the form:
+
+ {
+ status: 'PROGRESS', //Either FileUploader.Status.PROGRESS or FileUploader.Status.COMPLETE,
+ progress: 0, //The number of bytes uploaded so far.
+ total: 1000, //The total number of bytes to upload.
+ result: "OK" //The string returned from the server. Only sent when status = complete.
+ }
+
+This is under development, and the API is likely to change.
+
+
+## BUGS AND CONTRIBUTIONS ##
+The latest bleeding-edge version is available [on GitHub](http://github.com/ascorbic/phonegap-plugins/tree/master/Android/)
+If you have a patch, fork my repo baby and send me a pull request. Submit bug reports on GitHub, please.
+
+## Licence ##
+
+The MIT License
+
+Copyright (c) 2010 Matt Kane
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+
+
+
View
68 Android/FileUploader/fileuploader.js
@@ -0,0 +1,68 @@
+/**
+ * Phonegap File Upload plugin
+ * Copyright (c) Matt Kane 2010
+ *
+ */
+var FileUploader = function() {
+
+}
+
+
+/**
+ * Given a content:// uri, uploads the file to the server as a multipart/mime request
+ *
+ * @param server URL of the server that will receive the file
+ * @param file content:// uri of the file to upload
+ * @param fileKey Object with key: value params to send to the server
+ * @param params Parameter name of the file
+ * @param fileName Filename to send to the server. Defaults to image.jpg
+ * @param mimeType Mimetype of the uploaded file. Defaults to image/jpeg
+ * @param callback Success callback. Also receives progress messages during upload.
+ * @param fail Error callback
+ */
+FileUploader.prototype.uploadByUri = function(server, file, params, fileKey, fileName, mimeType, callback, fail) {
+
+ return PhoneGap.exec(function(args) {
+ callback(args);
+ }, function(args) {
+ if(typeof fail == 'function') {
+ fail(args);
+ }
+ }, 'FileUploader', 'uploadByUri', [server, file, params, fileKey, fileName, mimeType]);
+};
+
+/**
+ * Given absolute path, uploads the file to the server as a multipart/mime request
+ *
+ * @param server URL of the server that will receive the file
+ * @param file Absolute path of the file to upload
+ * @param fileKey Object with key: value params to send to the server
+ * @param params Parameter name of the file
+ * @param fileName Filename to send to the server. Defaults to image.jpg
+ * @param mimeType Mimetype of the uploaded file. Defaults to image/jpeg
+ * @param callback Success callback. Also receives progress messages during upload.
+ * @param fail Error callback
+ */
+FileUploader.prototype.upload = function(server, file, params, fileKey, fileName, mimeType, callback, fail) {
+
+ return PhoneGap.exec(function(args) {
+ if(typeof callback == 'function') {
+ callback(args);
+ }
+ }, function(args) {
+ if(typeof fail == 'function') {
+ fail(args);
+ }
+ }, 'FileUploader', 'upload', [server, file, params, fileKey, fileName, mimeType]);
+};
+
+
+FileUploader.Status = {
+ PROGRESS: "PROGRESS",
+ COMPLETE: "COMPLETE"
+}
+
+PhoneGap.addConstructor(function() {
+ PhoneGap.addPlugin('fileUploader', new FileUploader());
+ PluginManager.addService("FileUploader","com.beetight.FileUploader");
+});
View
2  iPhone/Badge/Badge.js
@@ -29,7 +29,7 @@ Badge.prototype.clear = function() {
PhoneGap.addConstructor(function()
{
- if(window.plugins)
+ if(!window.plugins)
{
window.plugins = {};
}
View
24 iPhone/BarcodeScanner/BarcodeScanner.h
@@ -0,0 +1,24 @@
+//
+// BarcodeScanner.h
+//
+// Created by Matt Kane on 12/01/2011.
+// Copyright 2011 Matt Kane. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "PhoneGapCommand.h"
+#import "ZXingWidgetController.h"
+#import "QRCodeReader.h"
+
+
+@interface BarcodeScanner : PhoneGapCommand <ZXingDelegate> {
+ NSString* successCallback;
+ NSString* failCallback;
+}
+
+@property (nonatomic, copy) NSString* successCallback;
+@property (nonatomic, copy) NSString* failCallback;
+
+- (void) scan:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+
+@end
View
51 iPhone/BarcodeScanner/BarcodeScanner.mm
@@ -0,0 +1,51 @@
+//
+// BarcodeScanner.m
+//
+// Created by Matt Kane on 12/01/2011.
+// Copyright 2011 Matt Kane. All rights reserved.
+//
+
+#import "BarcodeScanner.h"
+
+
+@implementation BarcodeScanner
+
+@synthesize successCallback, failCallback;
+
+- (void) scan:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+{
+ NSUInteger argc = [arguments count];
+
+ if (argc < 1) {
+ return;
+ }
+ self.successCallback = [arguments objectAtIndex:0];
+ if (argc > 1) {
+ self.failCallback = [arguments objectAtIndex:1];
+ }
+ //if (argc > 2) {
+ // TODO: Choose the readers to load
+ //}
+
+ ZXingWidgetController *widgetController = [[ZXingWidgetController alloc] initWithDelegate:self showCancel:YES OneDMode:NO];
+ QRCodeReader* qrcodeReader = [[QRCodeReader alloc] init];
+ NSSet *readers = [[NSSet alloc ] initWithObjects:qrcodeReader,nil];
+ [qrcodeReader release];
+ widgetController.readers = readers;
+ [readers release];
+ [[super appViewController ] presentModalViewController:widgetController animated:YES];
+ [widgetController release];
+}
+
+- (void)zxingController:(ZXingWidgetController*)controller didScanResult:(NSString *)result {
+ NSString* jsCallBack = [NSString stringWithFormat:@"%@(\"%@\");", self.successCallback, [result stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] ];
+ [self writeJavascript: jsCallBack];
+ [[super appViewController ] dismissModalViewControllerAnimated:NO];
+}
+
+- (void)zxingControllerDidCancel:(ZXingWidgetController*)controller {
+ [self writeJavascript: [NSString stringWithFormat:@"%@();", self.failCallback]];
+ [[super appViewController ] dismissModalViewControllerAnimated:YES];
+}
+
+@end
View
53 iPhone/BarcodeScanner/README.md
@@ -0,0 +1,53 @@
+# Barcode Scanner plugin for Phonegap #
+By Matt Kane
+
+## Adding the Plugin to your project ##
+Copy the .h and .mm file to the Plugins directory in your project. Copy the .js file to your www directory and reference it from your html file(s). You also need to add the ZXing library to your project.
+
+## Adding ZXing to your project ##
+First, [download the ZXing zip file](http://code.google.com/p/zxing/), unpack it, and put it somewhere safe such as your Documents folder. You'll need to whole package, not just the iphone folder. Then add it to your project, using the instructions in the zxing-1.x/iphone/README file.
+
+## Using the plugin ##
+The plugin creates the object `window.plugins.barcodeScanner` with one method `scan(types, success, fail)`
+`types` is a comma-separated list of barcode types that the scanner should accept. If you pass null then any
+barcode type will be accepted. Currently only `QR_CODE` is supported, but that should change soon.
+
+`success` and `fail` are callback functions. Success is passed the decoded barcode as a string.
+A full example could be:
+
+ window.plugins.barcodeScanner.scan( BarcodeScanner.Type.QR_CODE, function(result) {
+ alert("We got a barcode: " + result);
+ }, function(error) {
+ alert("Scanning failed: " + error);
+ }
+ );
+
+
+## BUGS AND CONTRIBUTIONS ##
+The latest bleeding-edge version is available [on GitHub](http://github.com/ascorbic/phonegap-plugins/tree/master/iPhone/)
+If you have a patch, fork my repo and send me a pull request. Submit bug reports on GitHub, please.
+
+## Licence ##
+
+The MIT License
+
+Copyright (c) 2011 Matt Kane
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
View
32 iPhone/BarcodeScanner/barcodescanner.js
@@ -0,0 +1,32 @@
+/**
+ * Phonegap Barcode Scanner plugin
+ * Copyright (c) Matt Kane 2011
+ *
+ */
+var BarcodeScanner = function() {
+
+}
+
+/* That's your lot for the moment */
+
+BarcodeScanner.Type = {
+ QR_CODE: "QR_CODE"
+}
+
+/* Types are ignored at the moment until I implement any other than QR Code */
+
+BarcodeScanner.prototype.scan = function(types, success, fail) {
+ return PhoneGap.exec("BarcodeScanner.scan", GetFunctionName(success), GetFunctionName(fail), types);
+};
+
+
+
+
+PhoneGap.addConstructor(function()
+{
+ if(!window.plugins)
+ {
+ window.plugins = {};
+ }
+ window.plugins.barcodeScanner = new BarcodeScanner();
+});
View
34 iPhone/FileUploader/FileUploader.h
@@ -0,0 +1,34 @@
+//
+// FileUploader.h
+//
+// Created by Matt Kane on 14/01/2011.
+// Copyright 2011 Matt Kane. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "PhoneGapCommand.h"
+
+@interface FileUploader : PhoneGapCommand {
+
+}
+- (void) upload:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+- (void) uploadByUri:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+- (void) uploadFile:(NSURL*)file toServer:(NSString*)server withParams:(NSMutableDictionary*)params fileKey:(NSString*)fileKey fileName:(NSString*)fileName mimeType:(NSString*)mimeType successCallback:(NSString*)successCallback failCallback:(NSString*)failCallback progressCallback:(NSString*)progressCallback;
+@end
+
+
+@interface FileUploadDelegate : NSObject {
+ NSString* successCallback;
+ NSString* failCallback;
+ NSString* progressCallback;
+ FileUploader* command;
+ int uploadIdx;
+}
+
+@property (nonatomic, copy) NSString* successCallback;
+@property (nonatomic, copy) NSString* failCallback;
+@property (nonatomic, copy) NSString* progressCallback;
+@property (nonatomic, retain) NSMutableData* responseData;
+@property (nonatomic, retain) FileUploader* command;
+
+@end;
View
223 iPhone/FileUploader/FileUploader.m
@@ -0,0 +1,223 @@
+//
+// FileUploader.m
+//
+// Created by Matt Kane on 14/01/2011.
+// Copyright 2011 Matt Kane. All rights reserved.
+//
+
+#import "FileUploader.h"
+
+
+@implementation FileUploader
+
+- (void) upload:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+{
+ NSUInteger argc = [arguments count];
+
+ if(argc < 2) {
+ return;
+ }
+
+ NSString* successCallback = [arguments objectAtIndex:0];
+ NSString* failCallback = [arguments objectAtIndex:1];
+
+ if(argc < 6) {
+ [self writeJavascript: [NSString stringWithFormat:@"%@(\"Argument error\");", failCallback]];
+ return;
+ }
+
+ NSString* progressCallback = [arguments objectAtIndex:2];
+ NSString* server = [arguments objectAtIndex:3];
+ NSURL* file = [NSURL fileURLWithPath:[arguments objectAtIndex:4] isDirectory: NO];
+ NSString* fileKey = nil;
+ NSString* fileName = nil;
+ NSString* mimeType = nil;
+
+ if(argc > 5) {
+ fileKey = [arguments objectAtIndex:5];
+ }
+
+ if(argc > 6) {
+ fileName = [arguments objectAtIndex:6];
+ }
+
+ if(argc > 7) {
+ mimeType = [arguments objectAtIndex:7];
+ }
+ [self uploadFile:file toServer:server withParams:options fileKey:fileKey fileName:fileName mimeType:mimeType successCallback:successCallback failCallback:failCallback progressCallback:progressCallback];
+}
+
+- (void) uploadByUri:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+{
+ NSUInteger argc = [arguments count];
+
+ if(argc < 2) {
+ return;
+ }
+
+ NSString* successCallback = [arguments objectAtIndex:0];
+ NSString* failCallback = [arguments objectAtIndex:1];
+
+ if(argc < 6) {
+ [self writeJavascript: [NSString stringWithFormat:@"%@(\"Argument error\");", failCallback]];
+ return;
+ }
+ NSString* progressCallback = [arguments objectAtIndex:2];
+ NSString* server = [arguments objectAtIndex:3];
+ NSURL* file = [NSURL URLWithString:[arguments objectAtIndex:4]];
+ NSString* fileKey = nil;
+ NSString* fileName = nil;
+ NSString* mimeType = nil;
+
+ if(argc > 5) {
+ fileKey = [arguments objectAtIndex:5];
+ }
+
+ if(argc > 6) {
+ fileName = [arguments objectAtIndex:6];
+ }
+
+ if(argc > 7) {
+ mimeType = [arguments objectAtIndex:7];
+ }
+ [self uploadFile:file toServer:server withParams:options fileKey:fileKey fileName:fileName mimeType:mimeType successCallback:successCallback failCallback:failCallback progressCallback:progressCallback];
+}
+
+- (void) uploadFile:(NSURL*)file toServer:(NSString*)server withParams:(NSMutableDictionary*)params fileKey:(NSString*)fileKey fileName:(NSString*)fileName mimeType:(NSString*)mimeType successCallback:(NSString*)successCallback failCallback:(NSString*)failCallback progressCallback:(NSString*)progressCallback
+{
+
+ if (![file isFileURL]) {
+ [self writeJavascript: [NSString stringWithFormat:@"%@(\"Is not a valid file\");", failCallback]];
+ return;
+ }
+
+ if(!fileName) {
+ fileName = @"image.jpg";
+ }
+
+ if(!mimeType) {
+ mimeType = @"image/jpeg";
+ }
+
+ if(!fileKey) {
+ fileKey = @"file";
+ }
+ NSURL *url = [NSURL URLWithString:server];
+
+ NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
+ [req setHTTPMethod:@"POST"];
+
+ if([params objectForKey:@"__cookie"]) {
+ [req setValue:[params objectForKey:@"__cookie"] forHTTPHeaderField:@"Cookie"];
+ [params removeObjectForKey:@"__cookie"];
+ [req setHTTPShouldHandleCookies:NO];
+ }
+
+ NSString *boundary = @"*****com.beetight.formBoundary";
+
+ NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
+ [req setValue:contentType forHTTPHeaderField:@"Content-type"];
+ [req setValue:@"XMLHttpRequest" forHTTPHeaderField:@"X-Requested-With"];
+ NSString* userAgent = [[webView request] valueForHTTPHeaderField:@"User-agent"];
+ if(userAgent) {
+ [req setValue: userAgent forHTTPHeaderField:@"User-agent"];
+ }
+
+ NSData *imageData = [NSData dataWithContentsOfURL:file];
+
+ if(!imageData) {
+ [self writeJavascript: [NSString stringWithFormat:@"%@(\"Could not open file\");", failCallback]];
+ return;
+ }
+
+ NSMutableData *postBody = [NSMutableData data];
+
+ NSEnumerator *enumerator = [params keyEnumerator];
+ id key;
+ id val;
+ while ((key = [enumerator nextObject])) {
+ val = [params objectForKey:key];
+ if(!val || val == [NSNull null]) {
+ continue;
+ }
+ [postBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
+ [postBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
+ [postBody appendData:[val dataUsingEncoding:NSUTF8StringEncoding]];
+ [postBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
+ }
+
+ [postBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
+ [postBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", fileKey, fileName] dataUsingEncoding:NSUTF8StringEncoding]];
+ [postBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", mimeType] dataUsingEncoding:NSUTF8StringEncoding]];
+ [postBody appendData:imageData];
+ [postBody appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
+ [req setHTTPBody:postBody];
+
+ FileUploadDelegate* delegate = [[FileUploadDelegate alloc] init];
+ delegate.command = self;
+ delegate.successCallback = successCallback;
+ delegate.failCallback = failCallback;
+ delegate.progressCallback = progressCallback;
+
+ [[NSURLConnection connectionWithRequest:req delegate:delegate] retain];
+}
+
+@end
+
+
+@implementation FileUploadDelegate
+
+@synthesize successCallback, failCallback, progressCallback, responseData, command;
+
+- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
+{
+ if(!self.progressCallback) {
+ return;
+ }
+ if (uploadIdx++ % 10 == 0) {
+ [command writeJavascript: [NSString stringWithFormat:@"%@(%d, %d);", self.progressCallback, totalBytesWritten, totalBytesExpectedToWrite]];
+ }
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection *)connection
+{
+ NSString* response = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
+ NSLog(@"reponse: %@", response);
+ NSString* js = [NSString stringWithFormat:@"%@(\"%@\");", self.successCallback, [response stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+ [command writeJavascript: js];
+ [connection autorelease];
+ [self autorelease];
+}
+
+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
+{
+ [command writeJavascript: [NSString stringWithFormat:@"%@(\"%@\");", self.failCallback, [[error localizedDescription] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
+ [connection autorelease];
+ [self autorelease];
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
+{
+ [responseData appendData:data];
+}
+
+- (id) init
+{
+ if (self = [super init]) {
+ self.responseData = [NSMutableData data];
+ uploadIdx = 0;
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ [successCallback release];
+ [failCallback release];
+ [responseData release];
+ [command release];
+ [super dealloc];
+}
+
+
+@end;
View
81 iPhone/FileUploader/README.md
@@ -0,0 +1,81 @@
+# File Uploader plugin for Phonegap - iPhone version #
+By Matt Kane
+
+Enables multipart/mime file uploads.
+
+## Adding the Plugin to your project ##
+
+Copy the .h and .m file to the Plugins directory in your project. Copy the .js file to your www directory and reference it from your html file(s).
+
+
+## Using the plugin ##
+The plugin creates the object `window.plugins.fileUploader` with two methods, `upload` and `uploadByUri`.
+These are identical except for the format of the reference to the file to upload. `upload` takes an
+absolute path, e.g. `/var/tmp/photo_001.jpg`, while `uploadByUri` takes a file:// Uri,
+e.g. `file://localhost/var/tmp/photo_001.jpg`.
+The full params are as follows:
+
+* server URL of the server that will receive the file
+* file Absolute path or uri of the file to upload
+* params Object with key: value params to send to the server
+* fileKey Parameter name of the file
+* fileName Filename to send to the server. Defaults to image.jpg
+* mimeType Mimetype of the uploaded file. Defaults to image/jpeg
+* success Success callback. Passed the response data from the server as a string.
+* fail Error callback. Passed the error message.
+* progress Called on upload progress. Signature should be function(bytesUploaded, totalBytes)
+
+Here is a simple example usage.
+
+ window.plugins.fileUploader.uploadByUri('http://example.com/upload', 'file://path/to/file.jpg', {foo: 'bar'}, 'myPhoto', 'anImage.jpg', 'image/jpeg',
+ function(result) {
+ console.log('Done: ' + result);
+ },
+ function(result) {
+ console.log("Error: " + result);
+ },
+ function(loaded, total) {
+ var percent = 100 / total * loaded;
+ console.log('Uploaded ' + percent);
+
+ }
+ );
+
+
+
+
+
+This is under development, and the API is likely to change.
+
+
+## BUGS AND CONTRIBUTIONS ##
+The latest bleeding-edge version is available [on GitHub](http://github.com/ascorbic/phonegap-plugins/)
+If you have a patch, fork my repo and send me a pull request. Submit bug reports on GitHub, please.
+
+## Licence ##
+
+The MIT License
+
+Copyright (c) 2011 Matt Kane
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+
+
View
76 iPhone/FileUploader/fileuploader.js
@@ -0,0 +1,76 @@
+/**
+ * Phonegap File Upload plugin
+ * iPhone version
+ * Copyright (c) Matt Kane 2011
+ *
+ */
+var FileUploader = function() {
+
+}
+
+
+/**
+ * Given a file:// url, uploads the file to the server as a multipart/mime request
+ *
+ * @param server URL of the server that will receive the file
+ * @param file file:// uri of the file to upload
+ * @param params Object with key: value params to send to the server
+ * @param fileKey Parameter name of the file
+ * @param fileName Filename to send to the server. Defaults to image.jpg
+ * @param mimeType Mimetype of the uploaded file. Defaults to image/jpeg
+ * @param success Success callback. Passed the response data from the server as a string.
+ * @param fail Error callback. Passed the error message.
+ * @param progress Called on upload progress. Signature should be function(bytesUploaded, totalBytes)
+ */
+FileUploader.prototype.uploadByUri = function(server, file, params, fileKey, fileName, mimeType, success, fail, progress) {
+ this._doUpload('uploadByUri', server, file, params, fileKey, fileName, mimeType, success, fail, progress);
+};
+
+/**
+ * Given absolute path, uploads the file to the server as a multipart/mime request
+ *
+ * @param server URL of the server that will receive the file
+ * @param file Absolute path of the file to upload
+ * @param params Object with key: value params to send to the server
+ * @param fileKey Parameter name of the file
+ * @param fileName Filename to send to the server. Defaults to image.jpg
+ * @param mimeType Mimetype of the uploaded file. Defaults to image/jpeg
+ * @param success Success callback. Passed the response data from the server as a string.
+ * @param fail Error callback. Passed the error message.
+ * @param progress Called on upload progress. Signature should be function(bytesUploaded, totalBytes)
+ */
+FileUploader.prototype.upload = function(server, file, params, fileKey, fileName, mimeType, success, fail, progress) {
+ this._doUpload('upload', server, file, params, fileKey, fileName, mimeType, success, fail, progress);
+};
+
+FileUploader.prototype._doUpload = function(method, server, file, params, fileKey, fileName, mimeType, success, fail, progress) {
+ if (!params) {
+ params = {}
+ }
+
+ var key = 'f' + this.callbackIdx++;
+ window.plugins.fileUploader.callbackMap[key] = {
+ success: function(result) {
+ success(result);
+ delete window.plugins.fileUploader.callbackMap[key]
+ },
+ fail: function(result) {
+ fail(result);
+ delete window.plugins.fileUploader.callbackMap[key]
+ },
+ progress: progress
+ }
+ var callback = 'window.plugins.fileUploader.callbackMap.' + key;
+
+ return PhoneGap.exec('FileUploader.' + method, callback + '.success', callback + '.fail', callback + '.progress', server, file, fileKey, fileName, mimeType, params);
+}
+
+FileUploader.prototype.callbackMap = {};
+FileUploader.prototype.callbackIdx = 0;
+
+PhoneGap.addConstructor(function() {
+ if(!window.plugins) {
+ window.plugins = {};
+ }
+ window.plugins.fileUploader = new FileUploader();
+});
View
38 iPhone/InAppPurchaseManager/InAppPurchaseManager.h
@@ -0,0 +1,38 @@
+//
+// InAppPurchaseManager.h
+// beetight
+//
+// Created by Matt Kane on 20/02/2011.
+// Copyright 2011 Matt Kane. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import <StoreKit/StoreKit.h>
+#import "PhoneGapCommand.h"
+#import "NSData+Base64.h"
+
+#import "SKProduct+LocalizedPrice.h"
+
+@interface InAppPurchaseManager : PhoneGapCommand <SKPaymentTransactionObserver> {
+
+}
+- (void) setup:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+- (void) makePurchase:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+- (void) requestProductData:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+- (void) paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions;
+
+@end
+
+@interface ProductsRequestDelegate : NSObject <SKProductsRequestDelegate>{
+ NSString* successCallback;
+ NSString* failCallback;
+
+ InAppPurchaseManager* command;
+}
+
+@property (nonatomic, copy) NSString* successCallback;
+@property (nonatomic, copy) NSString* failCallback;
+@property (nonatomic, retain) InAppPurchaseManager* command;
+
+@end;
+
View
153 iPhone/InAppPurchaseManager/InAppPurchaseManager.js
@@ -0,0 +1,153 @@
+/**
+ * A plugin to enable iOS In-App Purchases.
+ *
+ * Copyright (c) Matt Kane 2011
+ */
+
+var InAppPurchaseManager = function() {
+ PhoneGap.exec('InAppPurchaseManager.setup');
+}
+
+/**
+ * Makes an in-app purchase.
+ *
+ * @param {String} productId The product identifier. e.g. "com.example.MyApp.myproduct"
+ * @param {int} quantity
+ */
+
+InAppPurchaseManager.prototype.makePurchase = function(productId, quantity) {
+ var q = parseInt(quantity);
+ if(!q) {
+ q = 1;
+ }
+ return PhoneGap.exec('InAppPurchaseManager.makePurchase', productId, q);
+}
+
+/**
+ * Asks the payment queue to restore previously completed purchases.
+ * The restored transactions are passed to the onRestored callback, so make sure you define a handler for that first.
+ *
+ */
+
+InAppPurchaseManager.prototype.restoreCompletedTransactions = function() {
+ return PhoneGap.exec('InAppPurchaseManager.restoreCompletedTransactions');
+}
+
+
+/**
+ * Retrieves the localised product data, including price (as a localised string), name, description.
+ * You must call this before attempting to make a purchase.
+ *
+ * @param {String} productId The product identifier. e.g. "com.example.MyApp.myproduct"
+ * @param {Function} successCallback Called once for each returned product id. Signature is function(productId, title, description, price)
+ * @param {Function} failCallback Called once for each invalid product id. Signature is function(productId)
+ */
+
+InAppPurchaseManager.prototype.requestProductData = function(productId, successCallback, failCallback) {
+ var key = 'f' + this.callbackIdx++;
+ window.plugins.inAppPurchaseManager.callbackMap[key] = {
+ success: function(productId, title, description, price ) {
+ if (productId == '__DONE') {
+ delete window.plugins.fileUploader.callbackMap[key]
+ return;
+ }
+ successCallback(productId, title, description, price);
+ },
+ fail: failCallback
+ }
+ var callback = 'window.plugins.inAppPurchaseManager.callbackMap.' + key;
+ PhoneGap.exec('InAppPurchaseManager.requestProductData', productId, callback + '.success', callback + '.fail');
+}
+
+/* function(transactionIdentifier, productId, transactionReceipt) */
+InAppPurchaseManager.prototype.onPurchased = null;
+
+/* function(originalTransactionIdentifier, productId, originalTransactionReceipt) */
+InAppPurchaseManager.prototype.onRestored = null;
+
+/* function(errorCode, errorText) */
+InAppPurchaseManager.prototype.onFailed = null;
+
+
+/* This is called from native.*/
+
+InAppPurchaseManager.prototype.updatedTransactionCallback = function(state, errorCode, errorText, transactionIdentifier, productId, transactionReceipt) {
+ switch(state) {
+ case "PaymentTransactionStatePurchased":
+ if(this.onPurchased) {
+ this.onPurchased(transactionIdentifier, productId, transactionReceipt);
+ } else {
+ this.eventQueue.push(arguments);
+ this.watchQueue();
+ }
+ return;
+
+ case "PaymentTransactionStateFailed":
+ if(this.onFailed) {
+ this.onFailed(errorCode, errorText);
+ } else {
+ this.eventQueue.push(arguments);
+ this.watchQueue();
+ }
+ return;
+
+ case "PaymentTransactionStateRestored":
+ if(this.onRestored) {
+ this.onRestored(transactionIdentifier, productId, transactionReceipt);
+ } else {
+ this.eventQueue.push(arguments);
+ this.watchQueue();
+ }
+ return;
+ }
+}
+
+/*
+ * This queue stuff is here because we may be sent events before listeners have been registered. This is because if we have
+ * incomplete transactions when we quit, the app will try to run these when we resume. If we don't register to receive these
+ * right away then they may be missed. As soon as a callback has been registered then it will be sent any events waiting
+ * in the queue.
+ */
+
+InAppPurchaseManager.prototype.runQueue = function() {
+ if(!this.eventQueue.length || (!this.onPurchased && !this.onFailed && !this.onRestored)) {
+ return;
+ }
+ var args;
+ /* We can't work directly on the queue, because we're pushing new elements onto it */
+ var queue = this.eventQueue.slice();
+ this.eventQueue = [];
+ while(args = queue.shift()) {
+ this.updatedTransactionCallback.apply(this, args);
+ }
+ if(!this.eventQueue.length) {
+ this.unWatchQueue();
+ }
+}
+
+InAppPurchaseManager.prototype.watchQueue = function() {
+ if(this.timer) {
+ return;
+ }
+ this.timer = setInterval("window.plugins.inAppPurchaseManager.runQueue()", 10000);
+}
+
+InAppPurchaseManager.prototype.unWatchQueue = function() {
+ if(this.timer) {
+ clearInterval(this.timer);
+ this.timer = null;
+ }
+}
+
+
+InAppPurchaseManager.prototype.callbackMap = {};
+InAppPurchaseManager.prototype.callbackIdx = 0;
+InAppPurchaseManager.prototype.eventQueue = [];
+InAppPurchaseManager.prototype.timer = null;
+
+PhoneGap.addConstructor(function() {
+ if(!window.plugins) {
+ window.plugins = {};
+ }
+ window.plugins.inAppPurchaseManager = InAppPurchaseManager.manager = new InAppPurchaseManager();
+});
View
150 iPhone/InAppPurchaseManager/InAppPurchaseManager.m
@@ -0,0 +1,150 @@
+//
+// InAppPurchaseManager.m
+// beetight
+//
+// Created by Matt Kane on 20/02/2011.
+// Copyright 2011 Matt Kane. All rights reserved.
+//
+
+#import "InAppPurchaseManager.h"
+
+
+@implementation InAppPurchaseManager
+
+-(void) setup:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options {
+ [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
+}
+
+- (void) requestProductData:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+{
+ if([arguments count] < 3) {
+ return;
+ }
+ NSLog(@"Getting product data");
+ NSSet *productIdentifiers = [NSSet setWithObject:[arguments objectAtIndex:0]];
+ SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
+
+ ProductsRequestDelegate* delegate = [[[ProductsRequestDelegate alloc] init] retain];
+ delegate.command = self;
+ delegate.successCallback = [arguments objectAtIndex:1];
+ delegate.failCallback = [arguments objectAtIndex:2];
+
+ productsRequest.delegate = delegate;
+ [productsRequest start];
+
+}
+
+- (void) makePurchase:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+{
+ NSLog(@"About to do IAP");
+ if([arguments count] < 1) {
+ return;
+ }
+
+ SKMutablePayment *payment = [SKMutablePayment paymentWithProductIdentifier:[arguments objectAtIndex:0]];
+
+ if([arguments count] > 1) {
+ id quantity = [arguments objectAtIndex:1];
+ if ([quantity respondsToSelector:@selector(integerValue)]) {
+ payment.quantity = [quantity integerValue];
+ }
+ }
+ [[SKPaymentQueue defaultQueue] addPayment:payment];
+}
+
+- (void) restoreCompletedTransactions:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+{
+ [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
+}
+
+// SKPaymentTransactionObserver methods
+// called when the transaction status is updated
+//
+- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
+{
+ NSString *state, *error, *transactionIdentifier, *transactionReceipt, *productId;
+ NSInteger errorCode;
+
+ for (SKPaymentTransaction *transaction in transactions)
+ {
+ error = state = transactionIdentifier = transactionReceipt = productId = @"";
+ errorCode = 0;
+
+ switch (transaction.transactionState)
+ {
+ case SKPaymentTransactionStatePurchased:
+ state = @"PaymentTransactionStatePurchased";
+ transactionIdentifier = transaction.transactionIdentifier;
+ transactionReceipt = [[transaction transactionReceipt] base64EncodedString];
+ productId = transaction.payment.productIdentifier;
+ break;
+
+ case SKPaymentTransactionStateFailed:
+ state = @"PaymentTransactionStateFailed";
+ error = transaction.error.localizedDescription;
+ errorCode = transaction.error.code;
+ NSLog(@"error %d %@", errorCode, error);
+
+ break;
+
+ case SKPaymentTransactionStateRestored:
+ state = @"PaymentTransactionStateRestored";
+ transactionIdentifier = transaction.originalTransaction.transactionIdentifier;
+ transactionReceipt = [[[transaction originalTransaction] transactionReceipt] base64EncodedString];
+ productId = transaction.originalTransaction.payment.productIdentifier;
+ break;
+
+ default:
+ NSLog(@"Invalid state");
+ continue;
+ }
+ NSLog(@"state: %@", state);
+ NSString *js = [NSString stringWithFormat:@"InAppPurchaseManager.manager.updatedTransactionCallback('%@',%d, '%@','%@','%@','%@')", state, errorCode, error, transactionIdentifier, productId, transactionReceipt ];
+ NSLog(@"js: %@", js);
+ [self writeJavascript: js];
+ [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
+
+ }
+}
+
+
+@end
+
+@implementation ProductsRequestDelegate
+
+@synthesize successCallback, failCallback, command;
+
+
+- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
+{
+ NSLog(@"got iap product response");
+ for (SKProduct *product in response.products) {
+ NSLog(@"sending js for %@", product.productIdentifier);
+ NSString *js = [NSString stringWithFormat:@"%@('%@','%@','%@','%@')", successCallback, product.productIdentifier, product.localizedTitle, product.localizedDescription, product.localizedPrice];
+ NSLog(@"js: %@", js);
+ [command writeJavascript: js];
+ }
+
+ for (NSString *invalidProductId in response.invalidProductIdentifiers) {
+ NSLog(@"sending fail (%@) js for %@", failCallback, invalidProductId);
+
+ [command writeJavascript: [NSString stringWithFormat:@"%@('%@')", failCallback, invalidProductId]];
+ }
+ NSLog(@"done iap");
+
+ [command writeJavascript: [NSString stringWithFormat:@"%@('__DONE')", successCallback]];
+
+ [request release];
+ [self release];
+}
+
+- (void) dealloc
+{
+ [successCallback release];
+ [failCallback release];
+ [command release];
+ [super dealloc];
+}
+
+
+@end
View
97 iPhone/InAppPurchaseManager/README.md
@@ -0,0 +1,97 @@
+# iOS In-App Purchase plugin #
+By Matt Kane
+
+Allows In-App Purchases to be made from Phonegap. Wraps StoreKit.
+
+## Adding the Plugin to your project ##
+
+Copy the .h and .m file to the Plugins directory in your project. Copy the .js file to your www directory and reference it from your html file(s).
+
+
+## Using the plugin ##
+
+###NOTE: In-app purchases can be complicated, with very unhelpful error messages and lots of things that need to be configured perfectly for them to work. I cannot provide support for them. Errors are highly unlikely to have been caused by the plugin. Please see the Apple Developer Forums for help! ###
+
+Please read [the In-App Purchase Programming Guide](http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction/Introduction.html) and the [iTunes Connect Developer Guide](https://itunesconnect.apple.com/docs/iTunesConnect_DeveloperGuide.pdf).
+
+The plugin creates the object `window.plugins.inAppPurchaseManager` with the following methods:
+
+ requestProductData(productId, successCallback, failCallback)
+
+ makePurchase(productId, quantity)
+
+ restoreCompletedTransactions()
+
+You can also register the following callbacks:
+
+ window.plugins.inAppPurchaseManager.onPurchased = function(transactionIdentifier, productId, transactionReceipt) {}
+
+ window.plugins.inAppPurchaseManager.onRestored = function(originalTransactionIdentifier, productId, originalTransactionReceipt) {}
+
+ window.plugins.inAppPurchaseManager.onFailed = function(errorCode, errorText);
+
+You should register the callbacks early in your app's initialisation process, because StoreKit will automatically attempt to complete any unfinished transactions when you launch the app.
+If the plugin does receive callbacks before you have registered a handler, they will be placed into a queue and executed when you do register one.
+
+Before attempting to make a purchase you should first call `requestProductData` to retrieve the localised product data. If you don't do this, then any attempt to make a purchase will fail.
+A basic usage example is below:
+
+ window.plugins.inAppPurchaseManager.onPurchased = function(transactionIdentifier, productId, transactionReceipt) {
+ console.log('purchased: ' + productId);
+ /* Give coins, enable subscriptions etc */
+ }
+
+ window.plugins.inAppPurchaseManager.onRestored = function(transactionIdentifier, productId, transactionReceipt) {
+ console.log('restored: ' + productId);
+ /* See the developer guide for details of what to do with this */
+ }
+
+ window.plugins.inAppPurchaseManager.onError = function(errno, errtext) {
+ console.log('error: ' + errtext);
+ }
+
+ window.plugins.inAppPurchaseManager.requestProductData("com.example.test", function(productId, title, description, price) {
+ console.log("productId: " + productId + " title: " + title + " description: " + description + " price: " + price);
+ window.plugins.inAppPurchaseManager.makePurchase(productId, 1);
+ }, function(id) {
+ console.log("Invalid product id: " + id);
+ }
+ );
+
+
+
+
+
+## BUGS AND CONTRIBUTIONS ##
+For IAP support, please use [the Apple Developer Forum](https://devforums.apple.com/community/ios/integration/storekit).
+
+The latest bleeding-edge version is available [on GitHub](http://github.com/ascorbic/phonegap-plugins/)
+If you have a patch, fork my repo and send me a pull request. Submit bug reports on GitHub, please.
+
+## Licence ##
+
+The MIT License
+
+Copyright (c) 2011 Matt Kane
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+
+
View
10 iPhone/MapKitPlug/src/MapKit.m
@@ -37,6 +37,8 @@ - (void)createView
mapView.autoresizesSubviews = YES;
mapView.userInteractionEnabled = YES;
mapView.showsUserLocation = YES;
+ mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+ childView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
imageButton = [UIButton buttonWithType:UIButtonTypeCustom];
@@ -88,9 +90,7 @@ - (void)setMapData:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)opt
CGFloat height = 480.0f;
// default at bottom
BOOL atBottom = YES;
-
- NSArray *pins = [[NSArray alloc] init];
-
+
if ([options objectForKey:@"height"])
{
height=[[options objectForKey:@"height"] floatValue];
@@ -109,8 +109,8 @@ - (void)setMapData:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)opt
SBJSON *parser=[[SBJSON alloc] init];
- pins = [parser objectWithString:[arguments objectAtIndex:0]];
-
+ NSArray *pins = [parser objectWithString:[arguments objectAtIndex:0]];
+ [parser autorelease];
CGRect webViewBounds = webView.bounds;
CGRect mapBounds;
View
50 iPhone/SMSComposer/README.md
@@ -0,0 +1,50 @@
+# PhoneGap SMSComposer #
+by Grant Sanders
+
+## Adding the Plugin to your project ##
+
+Using this plugin requires [PhoneGap for iPhone](http://github.com/phonegap/phonegap-iphone).
+
+1. Add the SMSComposer.h and SMSComposer.m files to your "Plugins" folder in your PhoneGap project
+2. Add the SMSComposer.js files to your "www" folder on disk, and add a reference to the .js file after phonegap.js.
+
+## RELEASE NOTES ##
+
+### 201101112 ###
+* Initial release
+* Adds SMS text message composition in-app.
+* Requires iOS 4.0 or higher.
+ Attempts to compose SMS text without running 4.0+ fails gracefully with a friendly message.
+
+## EXAMPLE USAGE ##
+
+* All parameters are optional.
+window.plugins.smsComposer.showSMSComposer();
+
+
+* Passing phone number and message.
+window.plugins.smsComposer.showSMSComposer('3424221122', 'hello');
+
+* Multiple recipents are separated by comma(s).
+window.plugins.smsComposer.showSMSComposer('3424221122,2134463330', 'hello');
+
+
+* showSMSComposerWithCB takes a callback as its first parameter.
+* 0, 1, 2, or 3 will be passed to the callback when the text message has been attempted.
+
+window.plugins.smsComposer.showSMSComposerWithCB(function(result){
+
+ if(result == 0)
+ alert("Cancelled");
+ else if(result == 1)
+ alert("Sent");
+ else if(result == 2)
+ alert("Failed.");
+ else if(result == 3)
+ alert("Not Sent.");
+
+},'3424221122,2134463330', 'hello');
+
+
+* A fully working example as index.html has been added to this repository.
+* It is an example of what your www/index.html could look like.
View
13 iPhone/SMSComposer/SMSComposer.h
@@ -0,0 +1,13 @@
+//
+// SMSComposer.h
+//
+// Created by Grant Sanders on 12/25/2010.
+
+
+#import <Foundation/Foundation.h>
+#import "PhoneGapCommand.h"
+@interface SMSComposer : PhoneGapCommand {
+}
+
+- (void)showSMSComposer:(NSArray*)arguments withDict:(NSDictionary*)options;
+@end
View
51 iPhone/SMSComposer/SMSComposer.js
@@ -0,0 +1,51 @@
+/**
+ * Clipboard plugin for PhoneGap
+ * window.plugins.SMSComposer
+ *
+ * @constructor
+ */
+function SMSComposer()
+{
+ this.resultCallback = null;
+}
+
+SMSComposer.ComposeResultType =
+{
+Cancelled:0,
+Sent:1,
+Failed:2,
+NotSent:3
+}
+
+SMSComposer.prototype.showSMSComposer = function(toRecipients, body)
+{
+
+ var args = {};
+
+ if(toRecipients)
+ args.toRecipients = toRecipients;
+
+ if(body)
+ args.body = body;
+
+ PhoneGap.exec("SMSComposer.showSMSComposer",args);
+}
+
+SMSComposer.prototype.showSMSComposerWithCB = function(cbFunction,toRecipients,body)
+{
+ this.resultCallback = cbFunction;
+ this.showSMSComposer.apply(this,[toRecipients,body]);
+}
+
+SMSComposer.prototype._didFinishWithResult = function(res)
+{
+ this.resultCallback(res);
+}
+
+PhoneGap.addConstructor(function() {
+
+ if(!window.plugins) {
+ window.plugins = {};
+ }
+ window.plugins.smsComposer = new SMSComposer();
+});
View
85 iPhone/SMSComposer/SMSComposer.m
@@ -0,0 +1,85 @@
+//
+// ClipboardPlugin.m
+// Clipboard plugin for PhoneGap
+//
+// Created by Grant Sanders on 12/25/2010.
+//
+
+#import "SMSComposer.h"
+#import <MessageUI/MessageUI.h>
+#import <MessageUI/MFMessageComposeViewController.h>
+
+@implementation SMSComposer
+
+- (void)showSMSComposer:(NSArray*)arguments withDict:(NSDictionary*)options
+{
+
+ Class messageClass = (NSClassFromString(@"MFMessageComposeViewController"));
+ if (messageClass != nil) {
+
+ if (![messageClass canSendText]) {
+
+ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Notice" message:@"SMS Text not available."
+ delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
+ [alert show];
+ [alert release];
+ return;
+ }
+
+ } else {
+ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Notice" message:@"SMS Text not available."
+ delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
+ [alert show];
+ [alert release];
+ return;
+ }
+
+
+ NSString* body = [options valueForKey:@"body"];
+ NSString* toRecipientsString = [options valueForKey:@"toRecipients"];
+
+ MFMessageComposeViewController *picker = [[MFMessageComposeViewController alloc] init];
+ picker.messageComposeDelegate = self;
+
+ if(body != nil)
+ picker.body = [options valueForKey:@"body"];
+
+ if(toRecipientsString != nil)
+ [picker setRecipients:[ toRecipientsString componentsSeparatedByString:@","]];
+
+ [[ super appViewController ] presentModalViewController:picker animated:YES];
+ [picker release];
+
+}
+
+// Dismisses the composition interface when users tap Cancel or Send. Proceeds to update the message field with the result of the operation.
+- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
+{
+ // Notifies users about errors associated with the interface
+ int webviewResult = 0;
+
+ switch (result)
+ {
+ case MessageComposeResultCancelled:
+ webviewResult = 0;
+ break;
+ case MessageComposeResultSent:
+ webviewResult = 1;
+ break;
+ case MessageComposeResultFailed:
+ webviewResult = 2;
+ break;
+ default:
+ webviewResult = 3;
+ break;
+ }
+
+ [[ super appViewController ] dismissModalViewControllerAnimated:YES];
+
+ NSString* jsString = [[NSString alloc] initWithFormat:@"window.plugins.smsComposer._didFinishWithResult(%d);",webviewResult];
+ [self writeJavascript:jsString];
+ [jsString release];
+
+}
+
+@end
View
68 iPhone/SMSComposer/index.html
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+ <head>
+ <meta name="viewport" content="width=default-width; user-scalable=no" />
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+
+ <script type="text/javascript" charset="utf-8" src="phonegap.js"></script>
+
+ <script type="text/javascript" charset="utf-8" src="SMSComposer.js"></script>
+ <script type="text/javascript" charset="utf-8">
+
+ function onBodyLoad()
+ {
+ document.addEventListener("deviceready",onDeviceReady,false);
+ }
+ function onDeviceReady()
+ {
+ viewUpdate();
+ }
+ </script>
+ </head>
+ <body onload="onBodyLoad()">
+
+
+<input onclick="ComposeSMS();" type="button" value="Compose SMS text with number and body" /><br />
+<input onclick="ComposeSMS2();" type="button" value="Compose SMS text with no parameters" /><br />
+<input onclick="ComposeSMS3();" type="button" value="Compose SMS text to multiple recipients" /><br />
+<input onclick="ComposeSMSWithCallback();" type="button" value="Compose SMS text with callback" />
+
+
+<script>
+
+var ComposeSMS = function(){
+
+ window.plugins.smsComposer.showSMSComposer('3424221122', 'hello');
+}
+
+var ComposeSMS2 = function(){
+
+ window.plugins.smsComposer.showSMSComposer();
+}
+
+var ComposeSMS3 = function(){
+
+ window.plugins.smsComposer.showSMSComposer('3424221122,2134463330', 'hello');
+}
+
+var ComposeSMSWithCallback = function(){
+
+ window.plugins.smsComposer.showSMSComposerWithCB(myCallback,'3424221122,2134463330', 'hello');
+}
+
+var myCallback = function(result){
+
+if(result == 0)
+ alert("Cancelled");
+else if(result == 1)
+ alert("Sent");
+else if(result == 2)
+ alert("Failed.");
+else if(result == 3)
+ alert("Not Sent.");
+}
+
+</script>
+ </body>
+</html>
+
View
1  iPhone/ScreenShot/ScreenShot.h
@@ -7,6 +7,7 @@
//
#import <Foundation/Foundation.h>
+#import <QuartzCore/QuartzCore.h>
#import "PhoneGapCommand.h"
@interface Screenshot : PhoneGapCommand {
}
Please sign in to comment.
Something went wrong with that request. Please try again.