"&&!m?l.childNodes:[];for(g=n.length-1;g>=0;--g)p.nodeName(n[g],"tbody")&&!n[g].childNodes.length&&n[g].parentNode.removeChild(n[g])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l.parentNode.removeChild(l)}h.nodeType?t.push(h):p.merge(t,h)}l&&(h=l=s=null);if(!p.support.appendChecked)for(f=0;(h=t[f])!=null;f++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(f=0;(h=t[f])!=null;f++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[f+1,0].concat(r)),f+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.chrome?b.webkit=!0:b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^(none|table(?!-c[ea]).+)/,bO=/^margin/,bP=new RegExp("^("+q+")(.*)$","i"),bQ=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bR=new RegExp("^([-+])=("+q+")","i"),bS={},bT={position:"absolute",visibility:"hidden",display:"block"},bU={letterSpacing:0,fontWeight:400},bV=["Top","Right","Bottom","Left"],bW=["Webkit","O","Moz","ms"],bX=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return b$(this,!0)},hide:function(){return b$(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bX.apply(this,arguments):this.each(function(){(c?a:bZ(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bY(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bR.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bY(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bU&&(f=bU[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(b,c){var d,e,f,g,h=a.getComputedStyle(b,null),i=b.style;return h&&(d=h[c],d===""&&!p.contains(b.ownerDocument,b)&&(d=p.style(b,c)),bQ.test(d)&&bO.test(c)&&(e=i.width,f=i.minWidth,g=i.maxWidth,i.minWidth=i.maxWidth=i.width=d,d=h.width,i.width=e,i.minWidth=f,i.maxWidth=g)),d}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bQ.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth===0&&bN.test(bH(a,"display"))?p.swap(a,bT,function(){return cb(a,b,d)}):cb(a,b,d)},set:function(a,c,d){return b_(a,c,d?ca(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bQ.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bV[d]+b]=e[d]||e[d-2]||e[0];return f}},bO.test(a)||(p.cssHooks[a+b].set=b_)});var cd=/%20/g,ce=/\[\]$/,cf=/\r?\n/g,cg=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,ch=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ch.test(this.nodeName)||cg.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(cf,"\r\n")}}):{name:b.name,value:c.replace(cf,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ci(d,a[d],c,f);return e.join("&").replace(cd,"+")};var cj,ck,cl=/#.*$/,cm=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,co=/^(?:GET|HEAD)$/,cp=/^\/\//,cq=/\?/,cr=/
+
+
+See also the example project in the repository (note: you have to download and reference ActionBarSherlock yourself).
\ No newline at end of file
diff --git a/Android/ActionBarSherlockTabBar/assets/www/ActionBarSherlockTabBar.js b/Android/ActionBarSherlockTabBar/assets/www/ActionBarSherlockTabBar.js
new file mode 100644
index 0000000..b1980e7
--- /dev/null
+++ b/Android/ActionBarSherlockTabBar/assets/www/ActionBarSherlockTabBar.js
@@ -0,0 +1,47 @@
+/*
+ MIT licensed (http://www.opensource.org/licenses/mit-license.html)
+
+ See https://github.com/AndiDog/phonegap-plugins
+*/
+
+// Usage: actionBarSherlockTabBar = cordova.require('cordova/plugin/actionBarSherlockTabBar');
+cordova.define('cordova/plugin/actionBarSherlockTabBar', function(require, exports, module) {
+ var exec = require('cordova/exec')
+
+ var ActionBarSherlockTabBar = function() {}
+
+ ActionBarSherlockTabBar.prototype.hide = function() {
+ exec(null,
+ null,
+ 'ActionBarSherlockTabBar',
+ 'hide',
+ [])
+ }
+
+ ActionBarSherlockTabBar.prototype.show = function() {
+ exec(null,
+ null,
+ 'ActionBarSherlockTabBar',
+ 'show',
+ [])
+ }
+
+ ActionBarSherlockTabBar.prototype.setTabSelectedListener = function(callback) {
+ if(typeof callback != 'function')
+ throw 'ActionBarSherlockTabBar.setTabSelectedListener: Callback not a function'
+
+ exec(callback,
+ function() {},
+ 'ActionBarSherlockTabBar',
+ 'setTabSelectedListener',
+ [])
+ }
+
+ module.exports = new ActionBarSherlockTabBar()
+
+ exec(null,
+ null,
+ 'ActionBarSherlockTabBar',
+ '_init',
+ [])
+});
\ No newline at end of file
diff --git a/Android/ActionBarSherlockTabBar/src/de/andidog/phonegapplugins/ActionBarSherlockTabBarPlugin.java b/Android/ActionBarSherlockTabBar/src/de/andidog/phonegapplugins/ActionBarSherlockTabBarPlugin.java
new file mode 100644
index 0000000..298169b
--- /dev/null
+++ b/Android/ActionBarSherlockTabBar/src/de/andidog/phonegapplugins/ActionBarSherlockTabBarPlugin.java
@@ -0,0 +1,214 @@
+/*
+ MIT licensed (http://www.opensource.org/licenses/mit-license.html)
+
+ See https://github.com/AndiDog/phonegap-plugins
+*/
+package de.andidog.phonegapplugins;
+
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
+import org.json.JSONArray;
+
+import android.support.v4.app.FragmentTransaction;
+import android.util.Log;
+
+import com.actionbarsherlock.ActionBarSherlock;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.ActionBar.Tab;
+
+/**
+ * Acts like a bridge between the ActionBarSherlock tab bar implementation and the WebView. The code in the WebView can
+ * install a callback function that is called when the tab selection changes.
+ */
+public class ActionBarSherlockTabBarPlugin extends Plugin implements ActionBar.TabListener
+{
+ public interface OnInitListener
+ {
+ void onActionBarSherlockTabBarPluginInitialized();
+ }
+
+ private static final String TAG = "ActionBarSherlockTabBarPlugin";
+
+ public String callback;
+ private static OnInitListener onInitListener;
+
+ // https://issues.apache.org/jira/browse/CB-1062
+ public static ActionBarSherlockTabBarPlugin instance;
+
+ protected ActionBarSherlock sherlock;
+
+ public ActionBarSherlockTabBarPlugin()
+ {
+ instance = this;
+ }
+
+ /**
+ * Adds a tab to the tab bar implemented with ActionBarSherlock
+ *
+ * Use this from your main activity, it cannot be called from JavaScript code because you may want to reference
+ * resources (e.g. tab icons of R.drawable).
+ *
+ * @param tabTag
+ * @param text Resource ID for translated text label of the tab.
+ * @param icon Resource ID for a tab icon. Can be NULL, but either text or icon must be given.
+ */
+ public void addTab(String tabTag, int textResourceId, Integer iconResourceId)
+ {
+ addTab(tabTag, null, textResourceId, iconResourceId);
+ }
+
+ /**
+ * Adds a tab to the tab bar implemented with ActionBarSherlock
+ *
+ * Use this from your main activity, it cannot be called from JavaScript code because you may want to reference
+ * resources (e.g. tab icons of R.drawable).
+ *
+ * @param tabTag
+ * @param text Text label of the tab. Can be NULL, but either text or icon must be given.
+ * @param icon Resource ID for a tab icon. Can be NULL, but either text or icon must be given.
+ */
+ public void addTab(String tabTag, String text, Integer iconResourceId)
+ {
+ addTab(tabTag, text, null, iconResourceId);
+ }
+
+ protected void addTab(String tabTag, String text, Integer textResourceId, Integer iconResourceId)
+ {
+ if(sherlock == null)
+ throw new IllegalArgumentException("Must call setSherlock first");
+ if(tabTag == null)
+ throw new IllegalArgumentException("tabTag may not be NULL");
+ if((text == null && textResourceId == null) == (iconResourceId == null))
+ throw new IllegalArgumentException("Either text or icon must be set for a tab (not both)");
+
+ final ActionBar actionBar = sherlock.getActionBar();
+
+ Tab tab = actionBar.newTab();
+
+ if(text != null)
+ tab.setText(text);
+ else if(textResourceId != null)
+ tab.setText(textResourceId);
+ else if(iconResourceId != null)
+ tab.setIcon(iconResourceId);
+
+ tab.setTag(tabTag);
+ tab.setTabListener(this);
+
+ actionBar.addTab(tab);
+ }
+
+ public PluginResult execute(String action, JSONArray args, String callbackId)
+ {
+ if(action.equals("setTabSelectedListener"))
+ {
+ this.callback = callbackId;
+
+ if(args.length() != 0)
+ throw new AssertionError("setTabSelectedListener takes no arguments");
+
+ PluginResult res = new PluginResult(PluginResult.Status.NO_RESULT);
+ res.setKeepCallback(true);
+ return res;
+ }
+ else if(action.equals("hide"))
+ {
+ final ActionBar actionBar = sherlock.getActionBar();
+ cordova.getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run()
+ {
+ actionBar.hide();
+ }
+ });
+ return new PluginResult(PluginResult.Status.NO_RESULT);
+ }
+ else if(action.equals("show"))
+ {
+ final ActionBar actionBar = sherlock.getActionBar();
+ cordova.getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run()
+ {
+ actionBar.show();
+ }
+ });
+ return new PluginResult(PluginResult.Status.NO_RESULT);
+ }
+ else if(action.equals("_init"))
+ {
+ // This is the signal send from the JavaScript that forces construction of this plugin
+ if(onInitListener != null)
+ {
+ onInitListener.onActionBarSherlockTabBarPluginInitialized();
+ onInitListener = null;
+ }
+
+ return new PluginResult(PluginResult.Status.NO_RESULT);
+ }
+ else
+ {
+ Log.e(TAG, "Invalid call: " + action);
+ return new PluginResult(PluginResult.Status.INVALID_ACTION);
+ }
+ }
+
+ @Override
+ public void onTabReselected(Tab tab, FragmentTransaction ft)
+ {
+ Log.d(TAG, "Tab " + tab.getTag() + " reselected");
+ }
+
+ @Override
+ public void onTabSelected(Tab tab, FragmentTransaction ft)
+ {
+ triggerTabSelectedEvent((String)tab.getTag());
+
+ Log.d(TAG, "Tab " + tab.getTag() + " selected");
+ }
+
+ @Override
+ public void onTabUnselected(Tab tab, FragmentTransaction ft)
+ {
+ Log.d(TAG, "Tab " + tab.getTag() + " unselected");
+ }
+
+ /**
+ * Sets the activity that is enhanced with a tab bar
+ *
+ * This method must be run on the UI thread!
+ */
+ public void setSherlock(ActionBarSherlock sherlock)
+ {
+ if(this.sherlock != null && this.sherlock != sherlock)
+ throw new IllegalStateException("May only set ActionBarSherlock instance of tab bar once");
+
+ this.sherlock = sherlock;
+
+ final ActionBar actionBar = sherlock.getActionBar();
+
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+ actionBar.setDisplayShowHomeEnabled(false);
+ actionBar.setDisplayShowTitleEnabled(false);
+ }
+
+ // What you won't do to hack PhoneGap's plugin initialization order...
+ public static void setOnInitListener(OnInitListener listener)
+ {
+ onInitListener = listener;
+
+ // Trigger immediately if already loaded (probably won't happen)
+ if(instance != null)
+ {
+ onInitListener.onActionBarSherlockTabBarPluginInitialized();
+ onInitListener = null;
+ }
+ }
+
+ public void triggerTabSelectedEvent(String tabTag)
+ {
+ PluginResult res = new PluginResult(PluginResult.Status.OK, tabTag);
+ res.setKeepCallback(true);
+ this.success(res, callback);
+ }
+}
diff --git a/Android/Analytics/APACHE2.0LICENSE b/Android/Analytics/1.6/APACHE2.0LICENSE
similarity index 100%
rename from Android/Analytics/APACHE2.0LICENSE
rename to Android/Analytics/1.6/APACHE2.0LICENSE
diff --git a/Android/Analytics/LICENSE b/Android/Analytics/1.6/LICENSE
similarity index 100%
rename from Android/Analytics/LICENSE
rename to Android/Analytics/1.6/LICENSE
diff --git a/Android/Analytics/README.md b/Android/Analytics/1.6/README.md
similarity index 100%
rename from Android/Analytics/README.md
rename to Android/Analytics/1.6/README.md
diff --git a/Android/Analytics/config.xml b/Android/Analytics/1.6/config.xml
similarity index 100%
rename from Android/Analytics/config.xml
rename to Android/Analytics/1.6/config.xml
diff --git a/Android/Analytics/docs/Analytics.md b/Android/Analytics/1.6/docs/Analytics.md
similarity index 100%
rename from Android/Analytics/docs/Analytics.md
rename to Android/Analytics/1.6/docs/Analytics.md
diff --git a/Android/Analytics/lib/libGoogleAnalytics.jar b/Android/Analytics/1.6/lib/libGoogleAnalytics.jar
similarity index 100%
rename from Android/Analytics/lib/libGoogleAnalytics.jar
rename to Android/Analytics/1.6/lib/libGoogleAnalytics.jar
diff --git a/Android/Analytics/manifest b/Android/Analytics/1.6/manifest
similarity index 100%
rename from Android/Analytics/manifest
rename to Android/Analytics/1.6/manifest
diff --git a/Android/Analytics/src/com/phonegap/plugins/analytics/GoogleAnalyticsTracker.java b/Android/Analytics/1.6/src/com/phonegap/plugins/analytics/GoogleAnalyticsTracker.java
similarity index 100%
rename from Android/Analytics/src/com/phonegap/plugins/analytics/GoogleAnalyticsTracker.java
rename to Android/Analytics/1.6/src/com/phonegap/plugins/analytics/GoogleAnalyticsTracker.java
diff --git a/Android/Analytics/www/analytics.js b/Android/Analytics/1.6/www/analytics.js
similarity index 100%
rename from Android/Analytics/www/analytics.js
rename to Android/Analytics/1.6/www/analytics.js
diff --git a/Android/Analytics/2.0/APACHE2.0LICENSE b/Android/Analytics/2.0/APACHE2.0LICENSE
new file mode 100644
index 0000000..f347526
--- /dev/null
+++ b/Android/Analytics/2.0/APACHE2.0LICENSE
@@ -0,0 +1,62 @@
+libGoogleAnalytics.jar is distributed under Apache License, Version 2.0.
+The text of the Apache License, Version 2.0 licenses is reproduced below.
+
+----------------------------------------------
+
+Apache License, Version 2.0
+FoundationProjectsPeopleGet InvolvedDownloadSupport ApacheHome » Licenses
+Apache License
+
+Version 2.0, January 2004
+
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of this License; and
+
+You must cause any modified files to carry prominent notices stating that You changed the files; and
+
+You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+
+If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/Android/TTS/LICENSE b/Android/Analytics/2.0/LICENSE
similarity index 100%
rename from Android/TTS/LICENSE
rename to Android/Analytics/2.0/LICENSE
diff --git a/Android/Analytics/2.0/README.md b/Android/Analytics/2.0/README.md
new file mode 100644
index 0000000..2398f06
--- /dev/null
+++ b/Android/Analytics/2.0/README.md
@@ -0,0 +1,193 @@
+# Analytics plugin for Phonegap #
+
+The analytics client allows you to send page views to Google Analytics server.
+
+A simple use case would be:
+
+- Initialize Analytics object with the appropriate Google Analytics account.
+- Send page views upon user navigation.
+- Send events upon user interaction.
+
+## Adding the Plugin to your project ##
+
+Using this plugin requires [PhoneGap Cordova library for Android](http://phonegap.com/download) version 1.9 or above.
+
+1. To install the plugin, move www/analytics.js to your project's www folder and include a reference to it in your html file after cordova.js.
+
+ <script type="text/javascript" charset="utf-8" src="cordova.js"></script>
+ <script type="text/javascript" charset="utf-8" src="analytics.js"></script>
+
+2. Create a directory within your project called "src/com/phonegap/plugins/analytics" and copy src/com/phonegap/plugins/analytics/GoogleAnalyticsTracker.java into it.
+
+3. Add the following activity to your AndroidManifest.xml file if it not already there. It should be added inside the <application> tag.
+
+ <activity android:name="com.phonegap.DroidGap" android:label="@string/app_name">
+ <intent-filter>
+ </intent-filter>
+ </activity>
+
+4. Download [GoogleAnalytics](https://developers.google.com/analytics/devguides/collection/android/resources) library (tested with 1.4.2) and copy "lib/libGoogleAnalytics.jar" into the libs directory within your project. You will also need to right click on this file in eclipse and add the jar to the build path.
+
+5. In your res/xml/config.xml file add the following line:
+
+
+
+## Using the plugin ##
+
+The plugin creates the object `window.plugins.analytics`. To use, call one of the following, available methods:
+
+
+/**
+ * Initialize Google Analytics configuration
+ *
+ * @param accountId The Google Analytics account id
+ * @param successCallback The success callback
+ * @param failureCallback The error callback
+ */
+
+ start(accountId, successCallback, failureCallback);
+
+/**
+ * Track a page view on Google Analytics
+ * @param key The name of the tracked item (can be a url or some logical name).
+ * The key name will be presented in Google Analytics report.
+ * @param successCallback The success callback
+ * @param failureCallback The error callback
+ */
+
+ trackPageView(key, successCallback, failureCallback);
+
+/**
+ * Track an event on Google Analytics
+ * @param category The name that you supply as a way to group objects that you want to track
+ * @param action The name the type of event or interaction you want to track for a particular web object
+ * @param label Provides additional information for events that you want to track (optional)
+ * @param value Assign a numerical value to a tracked page object (optional)
+
+ * @param successCallback The success callback
+ * @param failureCallback The error callback
+ */
+
+ trackEvent(category, action, label, value, successCallback, failureCallback);
+
+
+Sample use:
+
+ window.plugins.analytics.trackEvent("category", "action", "event", 1, function(){alert("Track: success");}, function(){alert("Track: failure");});
+
+
+Please keep in mind that these methods, as in any other plugin, are ready to be invoked only after '[deviceready](http://docs.phonegap.com/phonegap_events_events.md.html#deviceready)' event has been fired
+Good practice will be manual dispatch and stop session. Add this code to your main activity:
+
+
+## RELEASE NOTES ##
+
+### AUG, 14, 2012 ###
+
+* Added suppport for Cordova 1.9 and above
+
+### AUG, 10, 2011 ###
+
+* Added event tracking
+
+### Jul 24, 2011 ###
+
+* Initial release
+
+## BUGS AND CONTRIBUTIONS ##
+
+## LICENSE ##
+
+PhoneGap is available under *either* the terms of the modified BSD license *or* the
+MIT License (2008). As a recipient of PhonegGap, you may choose which
+license to receive this code under (except as noted in per-module LICENSE
+files). Some modules may not be the copyright of Nitobi. These
+modules contain explicit declarations of copyright in both the LICENSE files in
+the directories in which they reside and in the code itself. No external
+contributions are allowed under licenses which are fundamentally incompatible
+with the MIT or BSD licenses that PhoneGap is distributed under.
+
+The text of the MIT and BSD licenses is reproduced below.
+
+---
+
+### The "New" BSD License
+
+Copyright (c) 2005-2010, Nitobi Software Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of Phonegap/Nitobi nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+### The MIT License
+
+Copyright (c) <2010>
+
+ 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.
+
+ ---
+
+ ### libGoogleAnalytics.jar
+
+ The libGoogleAnalytics.jar is distributed under Apache License, Version 2.0.
+ License URL: http://www.apache.org/licenses/LICENSE-2.0
+ libGoogleAnalytics.jar URL: http://code.google.com/p/android-scripting/source/browse/android/AndroidScriptingEnvironment/libs/libGoogleAnalytics.jar?r=41b40b84919bdf461784fd86e6ae464697d2abea
\ No newline at end of file
diff --git a/Android/Analytics/2.0/config.xml b/Android/Analytics/2.0/config.xml
new file mode 100644
index 0000000..3a5b3d7
--- /dev/null
+++ b/Android/Analytics/2.0/config.xml
@@ -0,0 +1,41 @@
+
+
+
+
+ Analytics plugin allows you to track page views using Google Analytics framework.
+
+
+
+The analytics client allows you to send page views to Google Analytics server.
+
+A simple use case would be:
+
+- Initialize Analytics object with the appropriate Google Analytics account.
+- Send page views upon user navigation.
+- Send event data upon user interaction.
+
+
+ Asaf Yishai
+ Kirill Danilov
+
+
+
+
+
+PhoneGap is available under *either* the terms of the modified BSD license *or* the
+MIT License (2008). As a recipient of PhonegGap, you may choose which
+license to receive this code under (except as noted in per-module LICENSE
+files). Some modules may not be the copyright of Nitobi. These
+modules contain explicit declarations of copyright in both the LICENSE files in
+the directories in which they reside and in the code itself. No external
+contributions are allowed under licenses which are fundamentally incompatible
+with the MIT or BSD licenses that PhoneGap is distributed under.
+
+The libGoogleAnalytics.jar is distributed under Apache License, Version 2.0.
+
+
\ No newline at end of file
diff --git a/Android/Analytics/2.0/docs/Analytics.md b/Android/Analytics/2.0/docs/Analytics.md
new file mode 100644
index 0000000..0f7f4a2
--- /dev/null
+++ b/Android/Analytics/2.0/docs/Analytics.md
@@ -0,0 +1,53 @@
+Analytics
+==========
+
+Analytics is an object that allows you to track page views using Google Analytics framework.
+
+Properties
+----------
+
+N/A
+
+Methods
+-------
+
+- start: Initialize Google Analytics with the appropriate Google Analytics account.
+- trackPageView: Track a page view on Google Analytics.
+
+
+Supported Platforms
+-------------------
+
+- Android
+
+Quick Example
+------------------------------
+
+ var onStartSuccess = function() {
+ alert("Google Analytics has started successfully");
+ }
+
+ var onStartFailure = function() {
+ alert("Google Analytics failed to start");
+ }
+
+ var onTrackSuccess = function() {
+ alert("A page view has been successfully sent to Google Analytics.");
+ }
+
+ var onTrackFailure = function() {
+ alert("A page view has failed to be submitted to Google Analytics");
+ }
+
+ var onEventSuccess = function() {
+ alert("An event has been successfully sent to Google Analytics.");
+ }
+
+ var onEventFailure = function() {
+ alert("An event has failed to be submitted to Google Analytics");
+ }
+
+ var myGoogleAnalyticsAccountId = "Your-Account-ID-Here"; // Get your account id from http://www.google.com/analytics/
+ window.plugins.analytics.start(myGoogleAnalyticsAccountId, onStartSuccess, onStartFailure);
+ window.plugins.analytics.trackPageView("page1.html", onTrackSuccess, onTrackFailure);
+ window.plugins.analytics.trackEvent("category", "action", "event", 1, onEventSuccess, onEventFailure);
diff --git a/Android/Analytics/2.0/lib/libGoogleAnalytics.jar b/Android/Analytics/2.0/lib/libGoogleAnalytics.jar
new file mode 100644
index 0000000..78d47a7
Binary files /dev/null and b/Android/Analytics/2.0/lib/libGoogleAnalytics.jar differ
diff --git a/Android/Analytics/2.0/manifest b/Android/Analytics/2.0/manifest
new file mode 100644
index 0000000..734770d
--- /dev/null
+++ b/Android/Analytics/2.0/manifest
@@ -0,0 +1,3 @@
+lib/libGoogleAnalytics.jar
+src/com/phonegap/plugins/analytics/GoogleAnalyticsTracker.java
+www/analytics.js
\ No newline at end of file
diff --git a/Android/Analytics/2.0/src/com/phonegap/plugins/analytics/GoogleAnalyticsTracker.java b/Android/Analytics/2.0/src/com/phonegap/plugins/analytics/GoogleAnalyticsTracker.java
new file mode 100644
index 0000000..4697f60
--- /dev/null
+++ b/Android/Analytics/2.0/src/com/phonegap/plugins/analytics/GoogleAnalyticsTracker.java
@@ -0,0 +1,82 @@
+/*
+ * PhoneGap is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ *
+ * Copyright (c) 2006-2011 Worklight, Ltd.
+ */
+
+package com.phonegap.plugins.analytics;
+
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
+import org.apache.cordova.api.PluginResult.Status;
+import org.json.JSONArray;
+import org.json.JSONException;
+
+import android.util.Log;
+
+public class GoogleAnalyticsTracker extends Plugin {
+ public static final String START = "start";
+ public static final String TRACK_PAGE_VIEW = "trackPageView";
+ public static final String TRACK_EVENT = "trackEvent";
+ public static final String SET_CUSTOM_VARIABLE = "setCustomVariable";
+
+ public static final int DISPATCH_INTERVAL = 20;
+ private com.google.android.apps.analytics.GoogleAnalyticsTracker tracker;
+
+ public GoogleAnalyticsTracker() {
+ tracker = com.google.android.apps.analytics.GoogleAnalyticsTracker.getInstance();
+ }
+
+ @Override
+ public PluginResult execute(String action, JSONArray data, String callbackId) {
+ PluginResult result = null;
+ if (START.equals(action)) {
+ try {
+ start(data.getString(0));
+ result = new PluginResult(Status.OK);
+ } catch (JSONException e) {
+ result = new PluginResult(Status.JSON_EXCEPTION);
+ }
+ } else if (TRACK_PAGE_VIEW.equals(action)) {
+ try {
+ trackPageView(data.getString(0));
+ result = new PluginResult(Status.OK);
+ } catch (JSONException e) {
+ result = new PluginResult(Status.JSON_EXCEPTION);
+ }
+ } else if (TRACK_EVENT.equals(action)) {
+ try {
+ trackEvent(data.getString(0), data.getString(1), data.getString(2), data.getInt(3));
+ result = new PluginResult(Status.OK);
+ } catch (JSONException e) {
+ result = new PluginResult(Status.JSON_EXCEPTION);
+ }
+ } else if (SET_CUSTOM_VARIABLE.equals(action)){
+ try {
+ setCustomVar(data.getInt(0), data.getString(1), data.getString(2), data.getInt(3));
+ } catch (JSONException e) {
+ result = new PluginResult(Status.JSON_EXCEPTION);
+ }
+ } else {
+ result = new PluginResult(Status.INVALID_ACTION);
+ }
+ return result;
+ }
+
+ private void start(String accountId) {
+ tracker.startNewSession(accountId, DISPATCH_INTERVAL, this.cordova.getActivity());
+ }
+
+ private void trackPageView(String key) {
+ tracker.trackPageView(key);
+ }
+
+ private void trackEvent(String category, String action, String label, int value){
+ tracker.trackEvent(category, action, label, value);
+ }
+
+ private void setCustomVar(int index, String label, String value, int scope) {
+ tracker.setCustomVar(index, label, value, scope);
+ }
+}
\ No newline at end of file
diff --git a/Android/Analytics/2.0/www/analytics.js b/Android/Analytics/2.0/www/analytics.js
new file mode 100644
index 0000000..0e41564
--- /dev/null
+++ b/Android/Analytics/2.0/www/analytics.js
@@ -0,0 +1,95 @@
+/*
+ * PhoneGap is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ *
+ * Copyright (c) 2006-2011 Worklight, Ltd.
+ */
+
+
+/**
+ * Constructor
+ */
+var Analytics = function () {};
+
+/**
+ * Initialize Google Analytics configuration
+ *
+ * @param accountId The Google Analytics account id
+ * @param successCallback The success callback
+ * @param failureCallback The error callback
+ */
+Analytics.prototype.start = function(accountId, successCallback, failureCallback) {
+ return cordova.exec(
+ successCallback,
+ failureCallback,
+ 'GoogleAnalyticsTracker',
+ 'start',
+ [accountId]);
+};
+
+/**
+ * Track a page view on Google Analytics
+ * @param key The name of the tracked item (can be a url or some logical name).
+ * The key name will be presented in Google Analytics report.
+ * @param successCallback The success callback
+ * @param failureCallback The error callback
+ */
+Analytics.prototype.trackPageView = function(key, successCallback, failureCallback) {
+ return cordova.exec(
+ successCallback,
+ failureCallback,
+ 'GoogleAnalyticsTracker',
+ 'trackPageView',
+ [key]);
+};
+
+/**
+ * Track an event on Google Analytics
+ * @param category The name that you supply as a way to group objects that you want to track
+ * @param action The name the type of event or interaction you want to track for a particular web object
+ * @param label Provides additional information for events that you want to track (optional)
+ * @param value Assign a numerical value to a tracked page object (optional)
+
+ * @param successCallback The success callback
+ * @param failureCallback The error callback
+ */
+
+Analytics.prototype.trackEvent = function(category, action, label, value, successCallback, failureCallback){
+ return cordova.exec(
+ successCallback,
+ failureCallback,
+ 'GoogleAnalyticsTracker',
+ 'trackEvent',
+ [
+ category,
+ action,
+ typeof label === "undefined" ? "" : label,
+ (isNaN(parseInt(value,10))) ? 0 : parseInt(value, 10)
+ ]);
+};
+
+Analytics.prototype.setCustomVar = function(index, label, value, scope, successCallback, failureCallback){
+ return cordova.exec(
+ successCallback,
+ failureCallback,
+ 'GoogleAnalyticsTracker',
+ 'setCustomVariable',
+ [
+ (isNaN(parseInt(index,10))) ? 0 : parseInt(index, 10),
+ label,
+ value,
+ (isNaN(parseInt(scope,10))) ? 0 : parseInt(scope, 10)
+ ]);
+};
+
+/**
+ * Load Analytics
+ */
+
+if(!window.plugins) {
+ window.plugins = {};
+}
+
+if (!window.plugins.analytics) {
+ window.plugins.analytics = new Analytics();
+}
diff --git a/Android/AppPreferences/assets/www/applicationPreferences.js b/Android/AppPreferences/1.8.1/assets/www/applicationPreferences.js
similarity index 100%
rename from Android/AppPreferences/assets/www/applicationPreferences.js
rename to Android/AppPreferences/1.8.1/assets/www/applicationPreferences.js
diff --git a/Android/AppPreferences/src/com/simonmacdonald/prefs/AppPreferences.java b/Android/AppPreferences/1.8.1/src/com/simonmacdonald/prefs/AppPreferences.java
similarity index 100%
rename from Android/AppPreferences/src/com/simonmacdonald/prefs/AppPreferences.java
rename to Android/AppPreferences/1.8.1/src/com/simonmacdonald/prefs/AppPreferences.java
diff --git a/Android/AppPreferences/2.0.0/README.md b/Android/AppPreferences/2.0.0/README.md
new file mode 100644
index 0000000..b0251a7
--- /dev/null
+++ b/Android/AppPreferences/2.0.0/README.md
@@ -0,0 +1,170 @@
+# Application Preferences plugin for Phonegap #
+Originally by Simon MacDonald (@macdonst)
+
+Please note that the following steps are for PhoneGap 2.0
+
+Information on writing plugins for PhoneGap 2.0 was taken from [this blog](http://simonmacdonald.blogspot.com/2012/08/so-you-wanna-write-phonegap-200-android.html) by Simon MacDonald (@macdonst)
+
+## Adding the Plugin to your project ##
+
+1) To install the plugin, move applicationPreferences.js to your project's www folder and include a reference to it in your html files.
+
+``
+
+2) Create a folder called 'com/simonmacdonald/prefs' within your project's src folder.
+3) And copy the AppPreferences.java file into that new folder.
+
+`mkdir /src/com/simonmacdonald/prefs`
+
+`cp ./src/com/simonmacdonald/prefs/AppPreferences.java /src/com/simonmacdonald/prefs`
+
+4) In your `res/xml/config.xml` file add the following element as a child to the `` element.
+
+ ``
+
+## Using the plugin ##
+
+Create an object to be used to call the defined plugin methods.
+
+ var preferences = cordova.require("cordova/plugin/applicationpreferences");
+
+The `preferences` object created above will be used in the following examples.
+
+### get ###
+
+In order to get the value a property you would call the get method.
+
+ /**
+ * Get the value of the named property.
+ *
+ * @param key
+ */
+ get(key, success, fail)
+
+Sample use:
+
+ preferences.get("myKey", function(value) {
+ alert("Value is " + value);
+ }, function(error) {
+ alert("Error! " + JSON.stringify(error));
+ });
+
+### set ###
+
+In order to set the value a property you would call the set method.
+
+ /**
+ * Set the value of the named property.
+ *
+ * @param key
+ * @param value
+ */
+ set(key, value, success, fail)
+
+Sample use:
+
+ preferences.set("myKey", "myValue", function() {
+ alert("Successfully saved!");
+ }, function(error) {
+ alert("Error! " + JSON.stringify(error));
+ });
+
+
+### remove ###
+
+In order to remove a key along with the value, you would call the remove method.
+
+ /**
+ * Remove the key along with the value
+ *
+ * @param key
+ */
+ remove(key, success, fail)
+
+Sample use:
+
+ preferences.remove("myKey", function(value) {
+ alert("Value removed!");
+ }, function(error) {
+ alert("Error! " + JSON.stringify(error));
+ });
+
+### clear ###
+
+In order to remove all shared preferences, you would call the clear method.
+
+ /**
+ * Clear all shared preferences
+ *
+ */
+ clear(success, fail)
+
+Sample use:
+
+ preferences.clear(function() {
+ alert("Cleared all preferences!");
+ }, function(error) {
+ alert("Error! " + JSON.stringify(error));
+ });
+
+### load ###
+
+In order to get all the properties you can call the load method. The success callback of the load method will be called with a JSONObject which contains all the preferences.
+
+ /**
+ * Get all the preference values.
+ *
+ */
+ load(success, fail)
+
+Sample use:
+
+ preferences.load(function(prefs) {
+ alert(JSON.stringify(prefs));
+ }, function() {
+ alert("Error! " + JSON.stringify(error));
+ });
+
+### show ###
+
+If you want to load the PreferenceActivity of your application that displays all the preferences you can call the show method with the class name.
+
+ /**
+ * Get all the preference values.
+ *
+ */
+ show(activity, success, fail)
+
+Sample use:
+
+ function showPreferenceActivity() {
+ preferences.show("com.ranhiru.apppreferences.PreferenceActivity", function() {
+ alert("Showing Preferences Activity!");
+ }, function(error) {
+ alert("Error! " + JSON.stringify(error));
+ });
+ }
+
+## Licence ##
+
+The MIT License
+
+Copyright (c) 2012 Simon MacDonald
+
+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.
\ No newline at end of file
diff --git a/Android/AppPreferences/2.0.0/assets/www/applicationPreferences.js b/Android/AppPreferences/2.0.0/assets/www/applicationPreferences.js
new file mode 100644
index 0000000..09f0c57
--- /dev/null
+++ b/Android/AppPreferences/2.0.0/assets/www/applicationPreferences.js
@@ -0,0 +1,39 @@
+cordova.define("cordova/plugin/applicationpreferences", function(require, exports, module) {
+ var exec = require("cordova/exec");
+ var AppPreferences = function () {};
+
+ var AppPreferencesError = function(code, message) {
+ this.code = code || null;
+ this.message = message || '';
+ };
+
+ AppPreferencesError.NO_PROPERTY = 0;
+ AppPreferencesError.NO_PREFERENCE_ACTIVITY = 1;
+
+ AppPreferences.prototype.get = function(key,success,fail) {
+ cordova.exec(success,fail,"applicationPreferences","get",[key]);
+ };
+
+ AppPreferences.prototype.set = function(key,value,success,fail) {
+ cordova.exec(success,fail,"applicationPreferences","set",[key, value]);
+ };
+
+ AppPreferences.prototype.load = function(success,fail) {
+ cordova.exec(success,fail,"applicationPreferences","load",[]);
+ };
+
+ AppPreferences.prototype.show = function(activity,success,fail) {
+ cordova.exec(success,fail,"applicationPreferences","show",[activity]);
+ };
+
+ AppPreferences.prototype.clear = function(success,fail) {
+ cordova.exec(success,fail,"applicationPreferences","clear", []);
+ };
+
+ AppPreferences.prototype.remove = function(keyToRemove, success,fail) {
+ cordova.exec(success,fail,"applicationPreferences","remove", [keyToRemove]);
+ };
+
+ var appPreferences = new AppPreferences();
+ module.exports = appPreferences;
+});
\ No newline at end of file
diff --git a/Android/AppPreferences/2.0.0/src/com/simonmacdonald/prefs/AppPreferences.java b/Android/AppPreferences/2.0.0/src/com/simonmacdonald/prefs/AppPreferences.java
new file mode 100644
index 0000000..268d382
--- /dev/null
+++ b/Android/AppPreferences/2.0.0/src/com/simonmacdonald/prefs/AppPreferences.java
@@ -0,0 +1,97 @@
+package com.simonmacdonald.prefs;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.preference.PreferenceManager;
+import android.util.Log;
+
+public class AppPreferences extends Plugin {
+
+ private static final String LOG_TAG = "AppPrefs";
+ private static final int NO_PROPERTY = 0;
+ private static final int NO_PREFERENCE_ACTIVITY = 1;
+
+ @Override
+ public PluginResult execute(String action, JSONArray args, String callbackId) {
+ PluginResult.Status status = PluginResult.Status.OK;
+ String result = "";
+
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this.cordova.getActivity());
+
+ try {
+ if (action.equals("get")) {
+ String key = args.getString(0);
+ if (sharedPrefs.contains(key)) {
+ Object obj = sharedPrefs.getAll().get(key);
+ return new PluginResult(status, obj.toString());
+ } else {
+ return createErrorObj(NO_PROPERTY, "No such property called " + key);
+ }
+ } else if (action.equals("set")) {
+ String key = args.getString(0);
+ String value = args.getString(1);
+ Editor editor = sharedPrefs.edit();
+ if ("true".equals(value.toLowerCase()) || "false".equals(value.toLowerCase())) {
+ editor.putBoolean(key, Boolean.parseBoolean(value));
+ } else {
+ editor.putString(key, value);
+ }
+ return new PluginResult(status, editor.commit());
+ } else if (action.equals("load")) {
+ JSONObject obj = new JSONObject();
+ Map prefs = sharedPrefs.getAll();
+ Iterator it = prefs.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry pairs = (Map.Entry)it.next();
+ obj.put(pairs.getKey().toString(), pairs.getValue().toString());
+ }
+ return new PluginResult(status, obj);
+ } else if (action.equals("show")) {
+ String activityName = args.getString(0);
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setClassName(this.cordova.getActivity(), activityName);
+ try {
+ this.cordova.getActivity().startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ return createErrorObj(NO_PREFERENCE_ACTIVITY, "No preferences activity called " + activityName);
+ }
+ } else if (action.equals("clear")) {
+ Editor editor = sharedPrefs.edit();
+ editor.clear();
+ return new PluginResult(status, editor.commit());
+ } else if (action.equals("remove")) {
+ String key = args.getString(0);
+ if (sharedPrefs.contains(key)) {
+ Editor editor = sharedPrefs.edit();
+ editor.remove(key);
+ return new PluginResult(status, editor.commit());
+ } else {
+ return createErrorObj(NO_PROPERTY, "No such property called " + key);
+ }
+
+ }
+ } catch (JSONException e) {
+ status = PluginResult.Status.JSON_EXCEPTION;
+ }
+ return new PluginResult(status, result);
+ }
+
+ private PluginResult createErrorObj(int code, String message) throws JSONException {
+ JSONObject errorObj = new JSONObject();
+ errorObj.put("code", code);
+ errorObj.put("message", message);
+ return new PluginResult(PluginResult.Status.ERROR, errorObj);
+ }
+
+}
diff --git a/Android/AugmentedReality-Wikitude/Documentation/Documentation.md b/Android/AugmentedReality-Wikitude/Documentation/Documentation.md
new file mode 100644
index 0000000..6e4b202
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/Documentation/Documentation.md
@@ -0,0 +1,116 @@
+## Wikitude PhoneGap Plugin Documentation
+by ``` Wikitude GmbH ```
+
+
+### DESCRIPTION
+***
+
+This document describes all available PhoneGap bindings which exists in the Wikitude PhoneGap Plugin.
+
+
+#### Getting information about the device
+
+###### isDeviceSupported
+Call ``` isDeviceSupported ``` to determinate if the current device is capable of launching ARchitect Worlds.
+
+ @param onSuccessCallback - A callback which gets called if the current device supports launching ARchitect Worlds
+ @param onErrorCallback - An callback which gets called if the current device does not fulfil all needs to lauch ARchitect Worlds
+ @param Plugin name - The name of the plugin you want to call a function
+ @param Plugin function - The name of the function you want to call on the plugin
+ @param options - No options
+
+ cordova.exec(deviceSupportedCallback, deviceNotSupportedCallback, "WikitudePlugin", "isDeviceSupported", [""]);
+
+
+#### Managing the ARchitectView
+
+
+###### open
+Call open to add the ARchitectView to your applications view hierarchy and loads the specified ARchitect World.
+
+ @param onSuccessCallback - A success callback
+ @param onErrorCallback - An error callback
+ @param Plugin name - The name of the plugin you want to call a function
+ @param Plugin function - The name of the function you want to call on the plugin
+ @param options - a dictionary containing two pairs:
+ @pair apiKey : The Wikitude SDK Key provided to you after you purchased the Wikitude SDK or an empty string if you're using a trial version
+ @pair filePath : A filepath to a local bundle resource or to a file on e.g. your dropbox
+
+ cordova.exec(architectWorldLaunchedCallback, architectWorldFailedLaunchingCallback, "WikitudePlugin", "open", [{ apiKey: myApiKey, filePath: worldPath}]);
+
+###### close
+This call will remove the ARchitectView from the view hierarchy and also stops all ongoing Wikitude updates.
+
+ @param onSuccessCallback - A success callback
+ @param onErrorCallback - An error callback
+ @param Plugin name - The name of the plugin you want to call a function
+ @param Plugin function - The name of the function you want to call on the plugin
+ @param options - No options
+
+ cordova.exec(onWikitudeOK, onWikitudeError, "WikitudePlugin", "close", [""]);
+
+###### show
+This call hides the ARchitectView, but keeps it alive in memory.
+
+ @param onSuccessCallback - A success callback
+ @param onErrorCallback - An error callback
+ @param Plugin name - The name of the plugin you want to call a function
+ @param Plugin function - The name of the function you want to call on the plugin
+ @param options - No options
+
+ cordova.exec(onWikitudeOK, onWikitudeError, "WikitudePlugin", "hide", [""]);
+
+###### hide
+This call will show the ARchitectView again if it was hidden with the ``` hide ``` call.
+
+ @param onSuccessCallback - A success callback
+ @param onErrorCallback - An error callback
+ @param Plugin name - The name of the plugin you want to call a function
+ @param Plugin function - The name of the function you want to call on the plugin
+ @param options - No options
+
+ cordova.exec(onWikitudeOK, onWikitudeError, "WikitudePlugin", "show", [""]);
+
+
+
+#### Interacting with the ARchitectView
+
+###### callJavaScript
+This function will evaluate the passed JavaScript in the current ARchitect World context.
+
+ @param onSuccessCallback - A success callback
+ @param onErrorCallback - An error callback
+ @param Plugin name - The name of the plugin you want to call a function
+ @param Plugin function - The name of the function you want to call on the plugin
+ @param options - A JavaScript array with a JavaScript string at first position
+
+ cordova.exec(onWikitudeOK, onWikitudeError, "WikitudePlugin", "callJavascript", [js]);
+
+###### onUrlInvoke
+This function will set an callback which gets called if you call ``` document.location = architectsdk://aRequestWithParameters?id=5&name=Poi5 ```.
+
+ @param onSuccessCallback - A callback which gets called every time you call ` architectsdk:// `
+ @param onErrorCallback - An error callback
+ @param Plugin name - The name of the plugin you want to call a function
+ @param Plugin function - The name of the function you want to call on the plugin
+ @param options - No options
+
+ cordova.exec(onUrlInvokeCallback, onWikitudeError, "WikitudePlugin", "onUrlInvoke", [""]);
+
+###### setLocation
+This function allows you to set a location for your current ARchitectView
+
+
+ @param onSuccessCallback - A success callback
+ @param onErrorCallback - An error callback
+ @param Plugin name - The name of the plugin you want to call a function
+ @param Plugin function - The name of the function you want to call on the plugin
+ @param options - A dictionary containing the new latitude, longitude, altitude, accuracy
+ @pair lat : The new latitude
+ @pair lon : The new longitude
+ @pair alt : The new altitude
+ @pair acc : The accuracy of the new locations
+
+ cordova.exec(onUrlInvokeCallback, onWikitudeError, "WikitudePlugin", "setLocation", [""]);
+
+
diff --git a/Android/AugmentedReality-Wikitude/Plugin/README.md b/Android/AugmentedReality-Wikitude/Plugin/README.md
new file mode 100644
index 0000000..559cb20
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/Plugin/README.md
@@ -0,0 +1,110 @@
+# Augmented Reality - Wikitude SDK Plugin
+by Wikitude GmbH - [www.wikitude.com](http://www.wikitude.com)
+
+Interested in advanced usage of the plugin and need assistance?
+[Register as a Wikitude developer](http://developer.wikitude.com) and get support in our forum.
+
+For further questions visit us on www.wikitude.com or contact us via `phonegap wikitude.com`
+
+#### Important: This plugin runs on latest [PhoneGap 2.x](http://docs.phonegap.com/en/2.0.0/guide_getting-started_android_index.md.html#Getting%20Started%20with%20Android) only
+
+
+## DESCRIPTION
+
+
+The Wikitude PhoneGap Plugin enables web developers to embed an Augmented Reality view into their PhoneGap project. One can create a fully featured app with advanced Augmented Reality features, including Image Recognition, purely using HTML, CSS and Javascript.
+
+###The Wikitude PhoneGap Plugin
+
+* Available for iOS and Android
+* Simple and seamless native PhoneGap integration
+* Fully customizable Augmented Realty view
+* Includes the full feature set of the Wikitude SDK
+* AR content is purely written in HTML and JavaScript
+
+
+
+###The Augmented Reality View
+From a technical point of view the SDK adds a UI component, similar to a web view. In contrast to a standard web view this AR view can render Augmented Reality content.
+
+Note: Content developed for this AR View is written in JavaScript and HTML. The .html and .js files for this view are different from the PhoneGap .js and .html files. The AR engine working in the background is called ARchitect Engine and is powering the SDK.
+
+###Further developer resources
+* [Full documentation and additional tutorials](http://forum.wikitude.com/documentation)
+* [Developer Forum](http://forum.wikitude.com/home)
+* [Wikitude SDK Download](http://forum.wikitude.com/download)
+* [Google+ Page for News](https://plus.google.com/u/0/103004921345651739447/posts)
+* [Developer Newsletter](http://www.wikitude.com/developer/newsletter)
+
+
+
+##Prerequisites
+* Having set-up [PhoneGap 2.x](http://docs.phonegap.com/en/2.0.0/guide_getting-
+ started_android_index.md.html#Getting%20Started%20with%20Android) project.
+* In case you didn't set `` or higher in `AndroidManifest.xml` be aware that the Wikitude SDK runs only on Android 2.2+ devices (=Android SDK v8); you must not call the plugin on devices with lower SDK version.
+
+## SETUP - 'Basic' Plugin
+
+
+1. Create a folder called `com/wikitude/phonegap` within your project's `src`- folder and copy `WikitudePlugin.java` into it
+
+2. Add following line to your `res/xml/config.xml`
+
+ ``
+
+3. Copy `WikitudePlugin.js` in `assets/www`-folder and ensure to include it in the related HTMLs.
+
+
+4. Download the [Wikitude SDK](http://www.wikitude.com), copy the wikitudesdk.jar in the Android-folder to your projects `libs`-folder and add it to your project's build path
+
+5. Visit [Wikitude Developer Site](http://developer.wikitude.com) to find Samples and license your app to get rid of the watermarking
+
+
+
+## Optional SETUP - 'Extended' Plugin (incl. Vuforia ImageRecognition)
+
+####In case you use ImageRecognition in your project, you need to use this Extended Plugin instead of the Basic One.
+
+Prerequisites: Having already set-up your [PhoneGap 2.x](http://docs.phonegap.com/en/2.0.0/guide_getting-started_android_index.md.html#Getting%20Started%20with%20Android) project.
+
+1. Create a folder called `com/wikitude/phonegap` within your project's `src/` folder and opy `WikitudePlugin.java` and `WikitudePluginVuforia.java` into it.
+
+2. Add following line to your `res/xml/config.xml`
+
+3. Copy `WikitudePlugin.js` in `assets/www`-folder and ensure to include it in the related HTMLs.
+
+ ``
+4. Download the [Wikitude SDK](http://www.wikitude.com), copy the wikitudesdk.jar in the Android-folder to your projects `libs`-folder and add it to your project's build path. Also copy the `libExtensionVuforia.so` into `libs/armeabi`
+
+5. Download Vuforia SDK from [Qualcomm Vuforia Website](https://ar.qualcomm.at/qdevnet/) and copy `QCAR.jar` to your projects `libs`-folder and add it to your project's build. Also copy Vuforia's `libQCAR.so` to your `libs/armeabi`-folder
+
+6. Visit [Wikitude Developer Site](http://developer.wikitude.com) to find Samples and license your app to get rid of the watermarking
+
+## JAVASCRIPT INTERFACE
+
+Its simple to use the Wikitude Plugin within your PhoneGap application.
+
+We wrapped all ``` cordova.exec ``` calls into a separate JavaScript wrapper which handles location updates and some more functionality behind the scenes.
+
+You will mainly work with the ``` WikitudePlugin ``` where all you have to do is to call ```Wikitude.isDeviceReady(successCallback, errorCallback)``` and in your successCallback, you can call ```WikitudePlugin.loadARchitectWorld(successCallback, errorCallback, "path/to/your/world")```.
+
+
+If you have purchased a Wikitude SDK license, you can enter you SDK Key in the ```WikitudePlugin.js``` file at line 9.
+
+
+## LICENSE
+
+ Copyright 2012 [Wikitude GmbH ](http://www.wikitude.com)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/Android/AugmentedReality-Wikitude/Plugin/WikitudePlugin.java b/Android/AugmentedReality-Wikitude/Plugin/WikitudePlugin.java
new file mode 100644
index 0000000..9082331
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/Plugin/WikitudePlugin.java
@@ -0,0 +1,427 @@
+package com.wikitude.phonegap;
+
+import java.io.IOException;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.app.Activity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.ViewManager;
+
+import com.phonegap.api.Plugin;
+import com.phonegap.api.PluginResult;
+import com.wikitude.architect.ArchitectUrlListener;
+import com.wikitude.architect.ArchitectView;
+import com.wikitude.architect.ArchitectView.ArchitectConfig;
+
+
+
+/**
+ * Basic PhoneGap Wikitude ARchitect Plugin
+ *
+ * You must add ""
+ * in config.xml to enable this plug-in in your project
+ *
+ * Also ensure to have wikitudesdk.jar in your libs folder
+ *
+ * Note:
+ * This plug-in is written under Apache License, Version 2.0
+ * http://www.apache.org/licenses/LICENSE-2.0.html
+ *
+ * @version 1.0.0
+ * @author Wikitude GmbH; www.wikitude.com
+ */
+public class WikitudePlugin extends Plugin implements ArchitectUrlListener {
+
+ /** PhoneGap-root to Android-app-assets folder ; e.g. use "assets/foo.html" as source if you want to load foo.html from your android-project's assets-folder */
+ private static final String LOCAL_ASSETS_PATH_ROOT = "assets/";
+
+ /* various JSON-Object keys*/
+ private static final String JSON_KEY_APIKEY = "sdkKey";
+ private static final String JSON_KEY_FILE_PATH = "filePath";
+
+ private static final String JSON_KEY_LOCATION_ALTITUDE = "alt";
+ private static final String JSON_KEY_LOCATION_ACCURACY = "acc";
+ private static final String JSON_KEY_LOCATION_LATITUDE = "lat";
+ private static final String JSON_KEY_LOCATION_LONGITUDE = "lon";
+
+ /* static action strings */
+
+ /**
+ * opens architect-view (add to view stack)
+ */
+ private static final String ACTION_OPEN = "open";
+
+ /**
+ * closes architect-view (remove view stack)
+ */
+ private static final String ACTION_CLOSE = "close";
+
+ /**
+ * set visibility of architectView to visible (of present)
+ */
+ private static final String ACTION_SHOW = "show";
+
+ /**
+ * set visibility of architectView to invisible (of present)
+ */
+ private static final String ACTION_HIDE = "hide";
+
+ /**
+ * inject location information
+ */
+ private static final String ACTION_SET_LOCATION = "setLocation";
+
+ /**
+ * callback for uri-invocations
+ */
+ private static final String ACTION_ON_URLINVOKE = "onUrlInvoke";
+
+ /**
+ * life-cycle notification for resume
+ */
+ private static final String ACTION_ON_RESUME = "onResume";
+
+ /**
+ * life-cycle notification for pause
+ */
+ private static final String ACTION_ON_PAUSE = "onPause";
+
+ /**
+ * check if view is on view-stack (no matter if visible or not)
+ */
+ private static final String ACTION_STATE_ISOPEN = "isOpen";
+
+ /**
+ * opens architect-view (add to view stack)
+ */
+ private static final String ACTION_IS_DEVICE_SUPPORTED = "isDeviceSupported";
+
+ /**
+ * check if view is on view-stack (no matter if visible or not)
+ */
+ private static final String ACTION_CALL_JAVASCRIPT = "callJavascript";
+
+ /**
+ * the Wikitude ARchitectview
+ */
+ private ArchitectView architectView;
+
+ /**
+ * callback-Id of url-invocation method
+ */
+ private String urlInvokeCallbackId = null;
+
+ /**
+ * callback-id of "open"-action method
+ */
+ private String openCallbackId = null;
+
+ @Override
+ public PluginResult execute( final String action, final JSONArray args, final String callbackId ) {
+
+ /* hide architect-view -> destroy and remove from activity */
+ if ( WikitudePlugin.ACTION_CLOSE.equals( action ) ) {
+ if ( this.architectView != null ) {
+ this.cordova.getActivity().runOnUiThread( new Runnable() {
+
+ @Override
+ public void run() {
+ removeArchitectView();
+ }
+ } );
+ return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" );
+ }
+ else {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" );
+ }
+ }
+
+ /* return success only if view is opened (no matter if visible or not) */
+ if ( WikitudePlugin.ACTION_STATE_ISOPEN.equals( action ) ) {
+ if ( this.architectView != null ) {
+ return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" );
+ } else {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" );
+ }
+ }
+
+ /* return success only if view is opened (no matter if visible or not) */
+ if ( WikitudePlugin.ACTION_IS_DEVICE_SUPPORTED.equals( action ) ) {
+ if ( ArchitectView.isDeviceSupported( this.cordova.getActivity() ) ) {
+ return new PluginResult( PluginResult.Status.OK, action + ": this device is ARchitect-ready" );
+ } else {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": Sorry, this device is NOT ARchitect-ready" );
+ }
+ }
+
+
+
+ /* life-cycle's RESUME */
+ if ( WikitudePlugin.ACTION_ON_RESUME.equals( action ) ) {
+
+ if ( this.architectView != null ) {
+ this.cordova.getActivity().runOnUiThread( new Runnable() {
+
+ @Override
+ public void run() {
+ WikitudePlugin.this.architectView.onResume();
+ }
+ } );
+
+ return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" );
+ }
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" );
+ }
+
+ /* life-cycle's PAUSE */
+ if ( WikitudePlugin.ACTION_ON_PAUSE.equals( action ) ) {
+ if ( architectView != null ) {
+ this.cordova.getActivity().runOnUiThread( new Runnable() {
+
+ @Override
+ public void run() {
+ WikitudePlugin.this.architectView.onPause();
+ }
+ } );
+
+ return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" );
+ }
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" );
+ }
+
+ /* set visibility to "visible", return error if view is null */
+ if ( WikitudePlugin.ACTION_SHOW.equals( action ) ) {
+
+ if ( this.architectView != null ) {
+ this.architectView.setVisibility( View.VISIBLE );
+ return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" );
+ } else {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" );
+ }
+ }
+
+ /* set visibility to "invisible", return error if view is null */
+ if ( WikitudePlugin.ACTION_HIDE.equals( action ) ) {
+
+ if ( this.architectView != null ) {
+ this.architectView.setVisibility( View.INVISIBLE );
+ return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" );
+ } else {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" );
+ }
+ }
+
+ /* define call-back for url-invocations */
+ if ( WikitudePlugin.ACTION_ON_URLINVOKE.equals( action ) ) {
+ this.urlInvokeCallbackId = callbackId;
+ PluginResult result = new PluginResult( PluginResult.Status.NO_RESULT, action + ": registered callback" );
+ result.setKeepCallback( true );
+ return result;
+ }
+
+ /* location update */
+ if ( WikitudePlugin.ACTION_SET_LOCATION.equals( action ) ) {
+ if ( this.architectView != null ) {
+ try {
+ String arrStr = args.getString( 0 );
+ JSONObject arr = new JSONObject( arrStr );
+ final double lat = arr.getDouble( WikitudePlugin.JSON_KEY_LOCATION_LATITUDE );
+ final double lon = arr.getDouble( WikitudePlugin.JSON_KEY_LOCATION_LONGITUDE );
+ Object altObj = arr.get( WikitudePlugin.JSON_KEY_LOCATION_ALTITUDE );
+ float alt = Float.MIN_VALUE;
+
+ if ( altObj != null && altObj instanceof Double ) {
+ alt = ((Double)altObj).floatValue();
+ }
+
+ final float altitude = alt;
+
+ final Double acc = arr.getDouble( WikitudePlugin.JSON_KEY_LOCATION_ACCURACY );
+ if ( this.cordova != null && this.cordova.getActivity() != null ) {
+ this.cordova.getActivity().runOnUiThread( new Runnable() {
+
+ @Override
+ public void run() {
+ if ( acc != null ) {
+ WikitudePlugin.this.architectView.setLocation( lat, lon, altitude, acc.floatValue() );
+ } else {
+ WikitudePlugin.this.architectView.setLocation( lat, lon, altitude );
+ }
+ }
+ } );
+ }
+
+ } catch ( Exception e ) {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": exception thrown, " + e != null ? e.getMessage() : "(exception is NULL)" );
+ }
+ return new PluginResult( PluginResult.Status.OK, action + ": updated location" );
+ }
+
+ /* return error if there is no architect-view active*/
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectview is not active" );
+ }
+
+ if ( WikitudePlugin.ACTION_CALL_JAVASCRIPT.equals( action ) ) {
+ if ( this.architectView != null ) {
+ String logMsg = null;
+ try {
+ final String callJS = args.getString( 0 );
+ logMsg = callJS;
+ this.cordova.getActivity().runOnUiThread( new Runnable() {
+
+ @Override
+ public void run() {
+ WikitudePlugin.this.architectView.callJavascript( callJS );
+ }
+ } );
+
+ } catch ( JSONException je ) {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": exception thrown, " + je != null ? je.getMessage() : "(exception is NULL)" );
+ }
+ return new PluginResult( PluginResult.Status.OK, action + ": called js, '" + logMsg + "'" );
+ } else {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectview is not active" );
+ }
+ }
+
+
+ /* initial set-up, show ArchitectView full-screen in current screen/activity */
+ if ( WikitudePlugin.ACTION_OPEN.equals( action ) ) {
+ this.openCallbackId = callbackId;
+ PluginResult result = null;
+ String arrStr = null;
+
+ try {
+ arrStr = args.getString( 0 );
+
+ // arrStr = arrStr.substring( 1, arrStr.length() - 1 );
+
+ JSONObject arr = new JSONObject( arrStr );
+
+ final String apiKey = arr.getString( WikitudePlugin.JSON_KEY_APIKEY );
+ final String filePath = arr.getString( WikitudePlugin.JSON_KEY_FILE_PATH );
+
+ this.cordova.getActivity().runOnUiThread( new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ WikitudePlugin.this.addArchitectView( apiKey, filePath );
+
+ /* call success method once architectView was added successfully */
+ if ( WikitudePlugin.this.openCallbackId != null ) {
+ PluginResult result = new PluginResult( PluginResult.Status.OK );
+ result.setKeepCallback( false );
+ WikitudePlugin.this.success( result, WikitudePlugin.this.openCallbackId );
+ }
+ } catch ( Exception e ) {
+ /* in case "addArchitectView" threw an exception -> notify callback method asynchronously */
+ WikitudePlugin.this.error( e != null ? e.getMessage() : "Exception is 'null'", WikitudePlugin.this.openCallbackId );
+ }
+ }
+ } );
+
+ } catch ( Exception e ) {
+ result = new PluginResult( PluginResult.Status.ERROR, action + ": exception thown, " + e != null ? e.getMessage() : "(exception is NULL)" );
+ result.setKeepCallback( false );
+ return result;
+ }
+
+ /* adding architect-view is done in separate thread, ensure to setKeepCallback so one can call success-method properly later on */
+ result = new PluginResult( PluginResult.Status.NO_RESULT, action + ": no result required, just registered callback-method" );
+ result.setKeepCallback( true );
+
+ return result;
+ }
+
+ /* fall-back return value */
+ return new PluginResult( PluginResult.Status.ERROR, "no such action: " + action );
+ }
+
+ /**
+ * called when url was invoked in architectView (by e.g. calling document.location = "myprotocoll://foo";
+ * @param url the invoked url (e.g. "myprotocoll://foo")
+ * @return true if call was handled properly
+ */
+ @Override
+ public boolean urlWasInvoked( String url ) {
+
+ /* call callback-method if set*/
+ if ( this.urlInvokeCallbackId != null ) {
+ try {
+ /* pass called url as String to callback-method */
+ PluginResult res = new PluginResult( PluginResult.Status.OK, url );
+ res.setKeepCallback( true );
+ this.success( res, this.urlInvokeCallbackId );
+ return true;
+ } catch ( Exception e ) {
+ this.error( "invalid url invoked: " + url, this.urlInvokeCallbackId );
+ }
+ }
+ return false;
+ }
+
+ /**
+ * hides/removes ARchitect-View completely
+ * @return true if successful, false otherwise
+ */
+ private boolean removeArchitectView() {
+ if ( this.architectView != null ) {
+ /* fake life-cycle calls, because activity is already up and running */
+ this.architectView.onPause();
+ this.architectView.onDestroy();
+ this.architectView.setVisibility( View.INVISIBLE );
+ ((ViewManager)this.architectView.getParent()).removeView( this.architectView );
+ this.architectView = null;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Architect-Configuration required for proper set-up
+ * @param apiKey
+ * @return
+ */
+ protected ArchitectConfig getArchitectConfig( final String apiKey ) {
+ /* no special set-up required in default Wikitude-Plugin, further things required in advanced usage (e.g. Vuforia Image Recognition) */
+ return new ArchitectConfig( apiKey );
+ }
+
+ /**
+ * add architectView to current screen
+ * @param apiKey developers's api key to use (hides watermarking/intro-animation if it matches your package-name)
+ * @param filePath the url (starting with http:// for online use; starting with LOCAL_ASSETS_PATH_ROOT if oyu want to load assets within your app-assets folder)
+ * @throws IOException might be thrown from ARchitect-SDK
+ */
+ @SuppressWarnings("deprecation")
+ private void addArchitectView( final String apiKey, String filePath ) throws IOException {
+ if ( this.architectView == null ) {
+ this.architectView = new ArchitectView( (Activity)this.ctx.getContext() );
+
+ /* add content view and fake initial life-cycle */
+ ((Activity)this.ctx.getContext()).addContentView( this.architectView, new ViewGroup.LayoutParams( LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT ) );
+
+ /* fake life-cycle calls, because activity is already up and running */
+ this.architectView.onCreate( getArchitectConfig( apiKey ) );
+ this.architectView.onPostCreate();
+
+ /* register self as url listener to fwd these native calls to PhoneGap */
+ this.architectView.registerUrlListener( WikitudePlugin.this );
+
+ /* load asset from local directory if prefix is used */
+ if ( filePath.startsWith( WikitudePlugin.LOCAL_ASSETS_PATH_ROOT ) ) {
+ filePath = filePath.substring( WikitudePlugin.LOCAL_ASSETS_PATH_ROOT.length() );
+ }
+ this.architectView.load( filePath );
+
+ /* also a fake-life-cycle call (the last one before it is really shown in UI */
+ this.architectView.onResume();
+ }
+ }
+}
diff --git a/Android/AugmentedReality-Wikitude/Plugin/WikitudePlugin.js b/Android/AugmentedReality-Wikitude/Plugin/WikitudePlugin.js
new file mode 100644
index 0000000..bd09eb2
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/Plugin/WikitudePlugin.js
@@ -0,0 +1,383 @@
+var WikitudePlugin = {
+
+ /**
+ *
+ * This is the SDK Key, provided to you after you purchased the Wikitude SDK from http://www.wikitude.com/developer/sdk
+ * If you're having a trial version, leave this string empty
+ *
+ */
+ mySDKKey : "ENTER-YOUR-KEY-HERE",
+
+ /** true if architectview is open */
+ isOpened : false,
+
+ /**
+ *
+ * called when user pressed back-button on Android device
+ *
+ */
+ backButtonCallback : null,
+
+ /**
+ *
+ * Change the value of this variable to modify the location update rate
+ *
+ */
+ locationUpdateRate : 3000,
+
+ /**
+ *
+ * This variable represents if the current device is capable of running the Wikitude SDK
+ *
+ */
+ isDeviceSupported : false,
+
+ /**
+ *
+ * This watchID is used to shedule location updates
+ *
+ */
+ watchID : null,
+
+ /**
+ *
+ * Callbacks to get device information if ARchitect Worlds can be launched
+ *
+ */
+ onDeviceSupportedCallback : null,
+ onDeviceNotSupportedCallback : null,
+
+ /**
+ *
+ * Callbacks to get notified if the ARchitect World finished launching or if something went wrong during the World launch
+ *
+ */
+ onARchitectWorldLaunchedCallback : null,
+ onARchitectWorldFailedLaunchingCallback : null,
+
+ /**
+ *
+ * This function gets called if PhoneGap reports that it has finished loading successfully.
+ *
+ */
+ isDeviceSupported: function(successCallback, errorCallback)
+ {
+
+ WikitudePlugin.onDeviceSupportedCallback = successCallback;
+ WikitudePlugin.onDeviceNotSupportedCallback = errorCallback;
+
+
+ // PhoneGap is running, so the first thing we do is to check if the current device is capable of running the Wikitude Plugin
+ cordova.exec(WikitudePlugin.deviceIsARchitectReady, WikitudePlugin.deviceIsNotARchitectReady, "WikitudePlugin", "isDeviceSupported", [""]);
+
+ },
+
+ /**
+ *
+ * Declare what should happen when user pressed back-button while architect-view is opened.
+ * If not declared/called or null: ARchitect-View is closed
+ *
+ */
+ onPressedBackButton : function(callback)
+ {
+ WikitudePlugin.backButtonCallback = callback;
+ },
+
+ /**
+ *
+ * This function gets called if the Wikitude Plugin reports that the device is able to start the Wikitude SDK
+ *
+ */
+ deviceIsARchitectReady : function()
+ {
+ // We keep track of the device status
+ WikitudePlugin.isDeviceSupported = true;
+
+
+ if(WikitudePlugin.onDeviceSupportedCallback)
+ {
+ WikitudePlugin.onDeviceSupportedCallback();
+ }
+ },
+
+ /**
+ *
+ * This function gets called if the Wikitude Plugin reports that the device is not able of starting the Wikitude SDK.
+ *
+ */
+ deviceIsNotARchitectReady : function()
+ {
+ WikitudePlugin.isDeviceSupported = false;
+
+ // In this case we notify the user that his device is not supported by the Wikitude SDK
+ if(WikitudePlugin.onDeviceNotSupportedCallback)
+ {
+ WikitudePlugin.onDeviceNotSupportedCallback();
+ }
+ },
+
+ /*
+ * =============================================================================================================================
+ *
+ * PUBLIC API
+ *
+ * =============================================================================================================================
+ */
+
+ /* Managing ARchitect world loading */
+
+ /**
+ *
+ * Call this function if you want to load an ARchitect World
+ *
+ * @param {String} worldPath The path to an ARchitect world ether on the device or on e.g. your dropbox
+ *
+ */
+ loadARchitectWorld : function(worldPath)
+ {
+
+ // before we actually call load, we check again if the device is able to open the world
+ if(WikitudePlugin.isDeviceSupported)
+ {
+
+ // the 'open' function of the Wikitude Plugin requires a option dictionary with two keys:
+ // @param {Object} options (required)
+ // @param {String} options.sdkKey License key for the Wikitude SDK
+ // @param {String} options.filePath The path to a local ARchitect world or to a ARchitect world on a server or your dropbox
+
+ cordova.exec(WikitudePlugin.worldLaunched, WikitudePlugin.worldFailedLaunching, "WikitudePlugin", "open", [{ sdkKey: WikitudePlugin.mySDKKey, filePath: worldPath}]);
+
+
+ // We add an event listener on the resume and pause event of the application lifecycle
+ document.addEventListener("resume", WikitudePlugin.onResume, false);
+ document.addEventListener("pause", WikitudePlugin.onPause, false);
+
+ WikitudePlugin.isOpened = true;
+ document.addEventListener("backbutton", WikitudePlugin.onBackButton, false);
+
+ // After we started loading the world, we start location updates
+ WikitudePlugin.startLocationUpdates();
+
+ }else
+ {
+ // if the device is not able to start the Wikitude SDK, we notify the user again
+ WikitudePlugin.deviceNotARchitectReady();
+ }
+ },
+
+ /* Managing the Wikitude SDK Lifecycle */
+
+ /**
+ *
+ * Use this function to stop the Wikitude SDK and to remove the ARchitectView from the screen
+ *
+ */
+ close : function()
+ {
+ document.removeEventListener("pause", WikitudePlugin.onPause, false);
+ document.removeEventListener("resume", WikitudePlugin.onResume, false);
+
+ WikitudePlugin.stopLocationUpdates();
+
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "close", [""]);
+ WikitudePlugin.isOpened = false;
+ },
+
+ /**
+ *
+ * Use this function to only hide the Wikitude SDK. All location and rendering updates are still active
+ *
+ */
+ hide : function()
+ {
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "hide", [""]);
+ },
+
+ /**
+ *
+ * Use this function to show the Wikitude SDK if it was hidden before
+ *
+ */
+ show : function()
+ {
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "show", [""]);
+ },
+
+ /* Interacting with the Wikitude SDK */
+
+ /**
+ *
+ * Use this function to call javascript which will be executed in the context of your ARchitect World
+ *
+ *
+ * @param js The JavaScript that gets evaluated in context of the ARchitect World
+ *
+ */
+ callJavaScript : function(js)
+ {
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "callJavascript", [js]);
+ },
+
+ /**
+ *
+ * Use this function to set a callback which will be invoked when the ARchitect World calls for example
+ * document.location = "architectsdk://opendetailpage?id=9";
+ *
+ *
+ * @param onUrlInvokeCallback A function which gets called when the ARchitect World invokes a call to "document.location = architectsdk://"
+ */
+ setOnUrlInvokeCallback : function(onUrlInvokeCallback)
+ {
+ cordova.exec(onUrlInvokeCallback, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onUrlInvoke", [""]);
+ },
+
+
+ /*
+ * =============================================================================================================================
+ *
+ * Callbacks of public functions
+ *
+ * =============================================================================================================================
+ */
+
+ /**
+ *
+ * Use this callback to get notified if the world loaded successfully
+ *
+ */
+ worldLaunched : function()
+ {
+ if(WikitudePlugin.onARchitectWorldLaunchedCallback)
+ {
+ WikitudePlugin.onARchitectWorldLaunchedCallback();
+ }
+ },
+
+ /**
+ *
+ * Use this callback to get notified if the Wikitude SDK wasn't able to load the ARchitect World
+ *
+ */
+ worldFailedLaunching : function(err)
+ {
+ if(WikitudePlugin.onARchitectWorldFailedLaunchingCallback)
+ {
+ WikitudePlugin.onARchitectWorldFailedLaunchingCallback(err);
+ }
+ },
+
+ /* Lifecycle updates */
+
+ /**
+ *
+ * This function actually starts the PhoneGap location updates
+ *
+ */
+ startLocationUpdates : function()
+ {
+
+ WikitudePlugin.watchID = navigator.geolocation.watchPosition(WikitudePlugin.onReceivedLocation, WikitudePlugin.onWikitudeError, { frequency: WikitudePlugin.locationUpdateRate });
+ },
+
+ /**
+ *
+ * This callback gets called everytime the location did update
+ *
+ */
+ onReceivedLocation : function(position)
+ {
+
+ // Every time that PhoneGap did received a location update, we pass the location into the Wikitude SDK
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "setLocation", [{ lat: position.coords.latitude, lon: position.coords.longitude, alt: position.coords.altitude, acc: position.coords.accuracy}]);
+ },
+
+ /**
+ *
+ * Use this function to stop location updates
+ *
+ */
+ stopLocationUpdates : function()
+ {
+
+ // We clear the location update watch which was responsible for updating the location in a specific time interval
+ navigator.geolocation.clearWatch(WikitudePlugin.watchID);
+ WikitudePlugin.watchID = null;
+ },
+
+ /**
+ *
+ * This function gets called every time the application did become active.
+ *
+ */
+ onResume : function()
+ {
+
+ // Call the Wikitude SDK that the application did become active again
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onResume", [""]);
+
+ // And start continuing updating the user location
+ WikitudePlugin.startLocationUpdates();
+ },
+
+ /**
+ *
+ * This function gets called every time the application is about to become inactive
+ *
+ */
+ onPause : function()
+ {
+
+ // Call the Wikitude SDK that the application did resign active
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onPause", [""]);
+
+ // And stop all ongoing location updates
+ WikitudePlugin.stopLocationUpdates();
+ },
+
+ /**
+ *
+ * Android specific!
+ * This function gets called if the user presses the back button.
+ * You may define your own implementation by using WikitudePlugin.onPressedBackButton(method)
+ *
+ */
+ onBackButton : function()
+ {
+ if (WikitudePlugin.isOpened) {
+ if (WikitudePlugin.backButtonCallback==null) {
+ WikitudePlugin.close();
+ document.removeEventListener("backbutton", WikitudePlugin.onBackButton, false);
+ } else {
+ WikitudePlugin.backButtonCallback();
+ }
+ }
+ },
+
+ /**
+ *
+ * This function gets called every time the application is about to become inactive
+ *
+ */
+ onWikitudeOK : function()
+ {
+ },
+
+ /**
+ *
+ * This function gets called every time the application is about to become inactive
+ *
+ */
+ onWikitudeError : function()
+ {
+ },
+
+ /**
+ *
+ * This function gets called every time the application is about to become inactive
+ *
+ */
+ report: function(id)
+ {
+ console.log("app report: " + id);
+ }
+};
diff --git a/Android/AugmentedReality-Wikitude/Plugin/WikitudePluginExtended.java b/Android/AugmentedReality-Wikitude/Plugin/WikitudePluginExtended.java
new file mode 100644
index 0000000..fb62e58
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/Plugin/WikitudePluginExtended.java
@@ -0,0 +1,89 @@
+package com.wikitude.phonegap;
+
+import android.app.Activity;
+
+import com.qualcomm.QCAR.QCAR;
+import com.wikitude.architect.ArchitectView.ArchitectConfig;
+import com.wikitude.architect.VuforiaInterface;
+
+
+
+/**
+ * Advanced Wikitude ARchitect Plugin (= Basic Plugin + Vuforia Image Recognition feature-set)
+ *
+ * You must add "wikitudesdk.jar" and "QCAR.lib" to your libs folder and build-path and have "libQCAR.so" in project's "libs/armeabi/" directory
+ *
+ * Also add ""
+ * (replace Basic-plugin entry if necessary)
+ *
+ * in config.xml to enable this plug-in in your project; Ensure your architectSDK key is Vuforia-ready
+ *
+ * Note:
+ * This plug-in is written under Apache License, Version 2.0
+ * http://www.apache.org/licenses/LICENSE-2.0.html
+ *
+ * @version 1.0.0
+ * @author Wikitude GmbH; www.wikitude.com
+ */
+public class WikitudePluginExtended extends WikitudePlugin {
+
+
+ static {
+ System.loadLibrary( "QCAR" );
+ }
+
+ /**
+ * Architect-Configuration required for proper set-up
+ * @param apiKey
+ * @return
+ */
+ @Override
+ protected ArchitectConfig getArchitectConfig( final String apiKey ) {
+ final ArchitectConfig config = super.getArchitectConfig( apiKey );
+
+ /* required for Vuforia */
+ config.setVuforiaInterface( new VuforiaServiceImplementation() );
+ return config;
+ }
+
+ /* required for Vuforia */
+ private class VuforiaServiceImplementation implements VuforiaInterface {
+
+ @Override
+ public void deInit() {
+ QCAR.deinit();
+ }
+
+ @Override
+ public int init() {
+ return QCAR.init();
+ }
+
+ @Override
+ public void onPause() {
+ QCAR.onPause();
+ }
+
+ @Override
+ public void onResume() {
+ QCAR.onResume();
+ }
+
+ @Override
+ public void onSurfaceChanged( final int arg0, final int arg1 ) {
+ QCAR.onSurfaceChanged( arg0, arg1 );
+ }
+
+ @Override
+ public void onSurfaceCreated() {
+ QCAR.onSurfaceCreated();
+ }
+
+ @Override
+ public void setInitParameters( final Activity activity, final int nFlags ) {
+ QCAR.setInitParameters( activity, nFlags );
+ }
+
+ }
+
+}
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/.classpath b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/.classpath
new file mode 100644
index 0000000..ce14813
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/.classpath
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/.project b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/.project
new file mode 100644
index 0000000..efb82dc
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/.project
@@ -0,0 +1,33 @@
+
+
+ HelloWorld
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/AndroidManifest.xml b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/AndroidManifest.xml
new file mode 100644
index 0000000..db22048
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/AndroidManifest.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/world/HelloWorld.html b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/world/HelloWorld.html
new file mode 100644
index 0000000..0b620f3
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/world/HelloWorld.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+My ARchitect World
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/cordova-2.0.0.js b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/cordova-2.0.0.js
new file mode 100644
index 0000000..ba9e6a9
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/cordova-2.0.0.js
@@ -0,0 +1,5724 @@
+// commit 114cf5304a74ff8f7c9ff1d21cf5652298af04b0
+
+// File generated at :: Wed Jul 18 2012 14:44:33 GMT-0700 (PDT)
+
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+;(function() {
+
+// file: lib/scripts/require.js
+var require,
+ define;
+
+(function () {
+ var modules = {};
+
+ function build(module) {
+ var factory = module.factory;
+ module.exports = {};
+ delete module.factory;
+ factory(require, module.exports, module);
+ return module.exports;
+ }
+
+ require = function (id) {
+ if (!modules[id]) {
+ throw "module " + id + " not found";
+ }
+ return modules[id].factory ? build(modules[id]) : modules[id].exports;
+ };
+
+ define = function (id, factory) {
+ if (modules[id]) {
+ throw "module " + id + " already defined";
+ }
+
+ modules[id] = {
+ id: id,
+ factory: factory
+ };
+ };
+
+ define.remove = function (id) {
+ delete modules[id];
+ };
+
+})();
+
+//Export for use in node
+if (typeof module === "object" && typeof require === "function") {
+ module.exports.require = require;
+ module.exports.define = define;
+}
+// file: lib/cordova.js
+define("cordova", function(require, exports, module) {
+var channel = require('cordova/channel');
+
+/**
+ * Listen for DOMContentLoaded and notify our channel subscribers.
+ */
+document.addEventListener('DOMContentLoaded', function() {
+ channel.onDOMContentLoaded.fire();
+}, false);
+if (document.readyState == 'complete' || document.readyState == 'interactive') {
+ channel.onDOMContentLoaded.fire();
+}
+
+/**
+ * Intercept calls to addEventListener + removeEventListener and handle deviceready,
+ * resume, and pause events.
+ */
+var m_document_addEventListener = document.addEventListener;
+var m_document_removeEventListener = document.removeEventListener;
+var m_window_addEventListener = window.addEventListener;
+var m_window_removeEventListener = window.removeEventListener;
+
+/**
+ * Houses custom event handlers to intercept on document + window event listeners.
+ */
+var documentEventHandlers = {},
+ windowEventHandlers = {};
+
+document.addEventListener = function(evt, handler, capture) {
+ var e = evt.toLowerCase();
+ if (typeof documentEventHandlers[e] != 'undefined') {
+ if (evt === 'deviceready') {
+ documentEventHandlers[e].subscribeOnce(handler);
+ } else {
+ documentEventHandlers[e].subscribe(handler);
+ }
+ } else {
+ m_document_addEventListener.call(document, evt, handler, capture);
+ }
+};
+
+window.addEventListener = function(evt, handler, capture) {
+ var e = evt.toLowerCase();
+ if (typeof windowEventHandlers[e] != 'undefined') {
+ windowEventHandlers[e].subscribe(handler);
+ } else {
+ m_window_addEventListener.call(window, evt, handler, capture);
+ }
+};
+
+document.removeEventListener = function(evt, handler, capture) {
+ var e = evt.toLowerCase();
+ // If unsubcribing from an event that is handled by a plugin
+ if (typeof documentEventHandlers[e] != "undefined") {
+ documentEventHandlers[e].unsubscribe(handler);
+ } else {
+ m_document_removeEventListener.call(document, evt, handler, capture);
+ }
+};
+
+window.removeEventListener = function(evt, handler, capture) {
+ var e = evt.toLowerCase();
+ // If unsubcribing from an event that is handled by a plugin
+ if (typeof windowEventHandlers[e] != "undefined") {
+ windowEventHandlers[e].unsubscribe(handler);
+ } else {
+ m_window_removeEventListener.call(window, evt, handler, capture);
+ }
+};
+
+function createEvent(type, data) {
+ var event = document.createEvent('Events');
+ event.initEvent(type, false, false);
+ if (data) {
+ for (var i in data) {
+ if (data.hasOwnProperty(i)) {
+ event[i] = data[i];
+ }
+ }
+ }
+ return event;
+}
+
+if(typeof window.console === "undefined") {
+ window.console = {
+ log:function(){}
+ };
+}
+
+var cordova = {
+ define:define,
+ require:require,
+ /**
+ * Methods to add/remove your own addEventListener hijacking on document + window.
+ */
+ addWindowEventHandler:function(event, opts) {
+ return (windowEventHandlers[event] = channel.create(event, opts));
+ },
+ addDocumentEventHandler:function(event, opts) {
+ return (documentEventHandlers[event] = channel.create(event, opts));
+ },
+ removeWindowEventHandler:function(event) {
+ delete windowEventHandlers[event];
+ },
+ removeDocumentEventHandler:function(event) {
+ delete documentEventHandlers[event];
+ },
+ /**
+ * Retreive original event handlers that were replaced by Cordova
+ *
+ * @return object
+ */
+ getOriginalHandlers: function() {
+ return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener},
+ 'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}};
+ },
+ /**
+ * Method to fire event from native code
+ */
+ fireDocumentEvent: function(type, data) {
+ var evt = createEvent(type, data);
+ if (typeof documentEventHandlers[type] != 'undefined') {
+ setTimeout(function() {
+ documentEventHandlers[type].fire(evt);
+ }, 0);
+ } else {
+ document.dispatchEvent(evt);
+ }
+ },
+ fireWindowEvent: function(type, data) {
+ var evt = createEvent(type,data);
+ if (typeof windowEventHandlers[type] != 'undefined') {
+ setTimeout(function() {
+ windowEventHandlers[type].fire(evt);
+ }, 0);
+ } else {
+ window.dispatchEvent(evt);
+ }
+ },
+ // TODO: this is Android only; think about how to do this better
+ shuttingDown:false,
+ UsePolling:false,
+ // END TODO
+
+ // TODO: iOS only
+ // This queue holds the currently executing command and all pending
+ // commands executed with cordova.exec().
+ commandQueue:[],
+ // Indicates if we're currently in the middle of flushing the command
+ // queue on the native side.
+ commandQueueFlushing:false,
+ // END TODO
+ /**
+ * Plugin callback mechanism.
+ */
+ callbackId: 0,
+ callbacks: {},
+ callbackStatus: {
+ NO_RESULT: 0,
+ OK: 1,
+ CLASS_NOT_FOUND_EXCEPTION: 2,
+ ILLEGAL_ACCESS_EXCEPTION: 3,
+ INSTANTIATION_EXCEPTION: 4,
+ MALFORMED_URL_EXCEPTION: 5,
+ IO_EXCEPTION: 6,
+ INVALID_ACTION: 7,
+ JSON_EXCEPTION: 8,
+ ERROR: 9
+ },
+
+ /**
+ * Called by native code when returning successful result from an action.
+ *
+ * @param callbackId
+ * @param args
+ */
+ callbackSuccess: function(callbackId, args) {
+ if (cordova.callbacks[callbackId]) {
+
+ // If result is to be sent to callback
+ if (args.status == cordova.callbackStatus.OK) {
+ try {
+ if (cordova.callbacks[callbackId].success) {
+ cordova.callbacks[callbackId].success(args.message);
+ }
+ }
+ catch (e) {
+ console.log("Error in success callback: "+callbackId+" = "+e);
+ }
+ }
+
+ // Clear callback if not expecting any more results
+ if (!args.keepCallback) {
+ delete cordova.callbacks[callbackId];
+ }
+ }
+ },
+
+ /**
+ * Called by native code when returning error result from an action.
+ *
+ * @param callbackId
+ * @param args
+ */
+ callbackError: function(callbackId, args) {
+ if (cordova.callbacks[callbackId]) {
+ try {
+ if (cordova.callbacks[callbackId].fail) {
+ cordova.callbacks[callbackId].fail(args.message);
+ }
+ }
+ catch (e) {
+ console.log("Error in error callback: "+callbackId+" = "+e);
+ }
+
+ // Clear callback if not expecting any more results
+ if (!args.keepCallback) {
+ delete cordova.callbacks[callbackId];
+ }
+ }
+ },
+ addConstructor: function(func) {
+ channel.onCordovaReady.subscribeOnce(function() {
+ try {
+ func();
+ } catch(e) {
+ console.log("Failed to run constructor: " + e);
+ }
+ });
+ }
+};
+
+// Register pause, resume and deviceready channels as events on document.
+channel.onPause = cordova.addDocumentEventHandler('pause');
+channel.onResume = cordova.addDocumentEventHandler('resume');
+channel.onDeviceReady = cordova.addDocumentEventHandler('deviceready');
+
+module.exports = cordova;
+
+});
+
+// file: lib/common/builder.js
+define("cordova/builder", function(require, exports, module) {
+var utils = require('cordova/utils');
+
+function each(objects, func, context) {
+ for (var prop in objects) {
+ if (objects.hasOwnProperty(prop)) {
+ func.apply(context, [objects[prop], prop]);
+ }
+ }
+}
+
+function include(parent, objects, clobber, merge) {
+ each(objects, function (obj, key) {
+ try {
+ var result = obj.path ? require(obj.path) : {};
+
+ if (clobber) {
+ // Clobber if it doesn't exist.
+ if (typeof parent[key] === 'undefined') {
+ parent[key] = result;
+ } else if (typeof obj.path !== 'undefined') {
+ // If merging, merge properties onto parent, otherwise, clobber.
+ if (merge) {
+ recursiveMerge(parent[key], result);
+ } else {
+ parent[key] = result;
+ }
+ }
+ result = parent[key];
+ } else {
+ // Overwrite if not currently defined.
+ if (typeof parent[key] == 'undefined') {
+ parent[key] = result;
+ } else if (merge && typeof obj.path !== 'undefined') {
+ // If merging, merge parent onto result
+ recursiveMerge(result, parent[key]);
+ parent[key] = result;
+ } else {
+ // Set result to what already exists, so we can build children into it if they exist.
+ result = parent[key];
+ }
+ }
+
+ if (obj.children) {
+ include(result, obj.children, clobber, merge);
+ }
+ } catch(e) {
+ utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"');
+ }
+ });
+}
+
+/**
+ * Merge properties from one object onto another recursively. Properties from
+ * the src object will overwrite existing target property.
+ *
+ * @param target Object to merge properties into.
+ * @param src Object to merge properties from.
+ */
+function recursiveMerge(target, src) {
+ for (var prop in src) {
+ if (src.hasOwnProperty(prop)) {
+ if (typeof target.prototype !== 'undefined' && target.prototype.constructor === target) {
+ // If the target object is a constructor override off prototype.
+ target.prototype[prop] = src[prop];
+ } else {
+ target[prop] = typeof src[prop] === 'object' ? recursiveMerge(
+ target[prop], src[prop]) : src[prop];
+ }
+ }
+ }
+ return target;
+}
+
+module.exports = {
+ build: function (objects) {
+ return {
+ intoButDontClobber: function (target) {
+ include(target, objects, false, false);
+ },
+ intoAndClobber: function(target) {
+ include(target, objects, true, false);
+ },
+ intoAndMerge: function(target) {
+ include(target, objects, true, true);
+ }
+ };
+ }
+};
+
+});
+
+// file: lib/common/channel.js
+define("cordova/channel", function(require, exports, module) {
+var utils = require('cordova/utils');
+
+/**
+ * Custom pub-sub "channel" that can have functions subscribed to it
+ * This object is used to define and control firing of events for
+ * cordova initialization.
+ *
+ * The order of events during page load and Cordova startup is as follows:
+ *
+ * onDOMContentLoaded Internal event that is received when the web page is loaded and parsed.
+ * onNativeReady Internal event that indicates the Cordova native side is ready.
+ * onCordovaReady Internal event fired when all Cordova JavaScript objects have been created.
+ * onCordovaInfoReady Internal event fired when device properties are available.
+ * onCordovaConnectionReady Internal event fired when the connection property has been set.
+ * onDeviceReady User event fired to indicate that Cordova is ready
+ * onResume User event fired to indicate a start/resume lifecycle event
+ * onPause User event fired to indicate a pause lifecycle event
+ * onDestroy Internal event fired when app is being destroyed (User should use window.onunload event, not this one).
+ *
+ * The only Cordova events that user code should register for are:
+ * deviceready Cordova native code is initialized and Cordova APIs can be called from JavaScript
+ * pause App has moved to background
+ * resume App has returned to foreground
+ *
+ * Listeners can be registered as:
+ * document.addEventListener("deviceready", myDeviceReadyListener, false);
+ * document.addEventListener("resume", myResumeListener, false);
+ * document.addEventListener("pause", myPauseListener, false);
+ *
+ * The DOM lifecycle events should be used for saving and restoring state
+ * window.onload
+ * window.onunload
+ *
+ */
+
+/**
+ * Channel
+ * @constructor
+ * @param type String the channel name
+ * @param opts Object options to pass into the channel, currently
+ * supports:
+ * onSubscribe: callback that fires when
+ * something subscribes to the Channel. Sets
+ * context to the Channel.
+ * onUnsubscribe: callback that fires when
+ * something unsubscribes to the Channel. Sets
+ * context to the Channel.
+ */
+var Channel = function(type, opts) {
+ this.type = type;
+ this.handlers = {};
+ this.numHandlers = 0;
+ this.guid = 1;
+ this.fired = false;
+ this.enabled = true;
+ this.events = {
+ onSubscribe:null,
+ onUnsubscribe:null
+ };
+ if (opts) {
+ if (opts.onSubscribe) this.events.onSubscribe = opts.onSubscribe;
+ if (opts.onUnsubscribe) this.events.onUnsubscribe = opts.onUnsubscribe;
+ }
+},
+ channel = {
+ /**
+ * Calls the provided function only after all of the channels specified
+ * have been fired.
+ */
+ join: function (h, c) {
+ var i = c.length;
+ var len = i;
+ var f = function() {
+ if (!(--i)) h();
+ };
+ for (var j=0; j 0) {
+ var v = JSON.parse(r);
+
+ // If status is OK, then return value back to caller
+ if (v.status === cordova.callbackStatus.OK) {
+
+ // If there is a success callback, then call it now with
+ // returned value
+ if (success) {
+ try {
+ success(v.message);
+ } catch (e) {
+ console.log("Error in success callback: " + callbackId + " = " + e);
+ }
+
+ // Clear callback if not expecting any more results
+ if (!v.keepCallback) {
+ delete cordova.callbacks[callbackId];
+ }
+ }
+ return v.message;
+ }
+
+ // If no result
+ else if (v.status === cordova.callbackStatus.NO_RESULT) {
+ // Clear callback if not expecting any more results
+ if (!v.keepCallback) {
+ delete cordova.callbacks[callbackId];
+ }
+ }
+
+ // If error, then display error
+ else {
+ console.log("Error: Status="+v.status+" Message="+v.message);
+
+ // If there is a fail callback, then call it now with returned value
+ if (fail) {
+ try {
+ fail(v.message);
+ }
+ catch (e1) {
+ console.log("Error in error callback: "+callbackId+" = "+e1);
+ }
+
+ // Clear callback if not expecting any more results
+ if (!v.keepCallback) {
+ delete cordova.callbacks[callbackId];
+ }
+ }
+ return null;
+ }
+ }
+ } catch (e2) {
+ console.log("Error: "+e2);
+ }
+};
+
+});
+
+// file: lib/android/platform.js
+define("cordova/platform", function(require, exports, module) {
+module.exports = {
+ id: "android",
+ initialize:function() {
+ var channel = require("cordova/channel"),
+ cordova = require('cordova'),
+ callback = require('cordova/plugin/android/callback'),
+ polling = require('cordova/plugin/android/polling'),
+ exec = require('cordova/exec');
+
+ channel.onDestroy.subscribe(function() {
+ cordova.shuttingDown = true;
+ });
+
+ // Start listening for XHR callbacks
+ // Figure out which bridge approach will work on this Android
+ // device: polling or XHR-based callbacks
+ setTimeout(function() {
+ if (cordova.UsePolling) {
+ polling();
+ }
+ else {
+ var isPolling = prompt("usePolling", "gap_callbackServer:");
+ cordova.UsePolling = isPolling;
+ if (isPolling == "true") {
+ cordova.UsePolling = true;
+ polling();
+ } else {
+ cordova.UsePolling = false;
+ callback();
+ }
+ }
+ }, 1);
+
+ // Inject a listener for the backbutton on the document.
+ var backButtonChannel = cordova.addDocumentEventHandler('backbutton', {
+ onSubscribe:function() {
+ // If we just attached the first handler, let native know we need to override the back button.
+ if (this.numHandlers === 1) {
+ exec(null, null, "App", "overrideBackbutton", [true]);
+ }
+ },
+ onUnsubscribe:function() {
+ // If we just detached the last handler, let native know we no longer override the back button.
+ if (this.numHandlers === 0) {
+ exec(null, null, "App", "overrideBackbutton", [false]);
+ }
+ }
+ });
+
+ // Add hardware MENU and SEARCH button handlers
+ cordova.addDocumentEventHandler('menubutton');
+ cordova.addDocumentEventHandler('searchbutton');
+
+ // Figure out if we need to shim-in localStorage and WebSQL
+ // support from the native side.
+ var storage = require('cordova/plugin/android/storage');
+
+ // First patch WebSQL if necessary
+ if (typeof window.openDatabase == 'undefined') {
+ // Not defined, create an openDatabase function for all to use!
+ window.openDatabase = storage.openDatabase;
+ } else {
+ // Defined, but some Android devices will throw a SECURITY_ERR -
+ // so we wrap the whole thing in a try-catch and shim in our own
+ // if the device has Android bug 16175.
+ var originalOpenDatabase = window.openDatabase;
+ window.openDatabase = function(name, version, desc, size) {
+ var db = null;
+ try {
+ db = originalOpenDatabase(name, version, desc, size);
+ }
+ catch (ex) {
+ if (ex.code === 18) {
+ db = null;
+ } else {
+ throw ex;
+ }
+ }
+
+ if (db === null) {
+ return storage.openDatabase(name, version, desc, size);
+ }
+ else {
+ return db;
+ }
+
+ };
+ }
+
+ // Patch localStorage if necessary
+ if (typeof window.localStorage == 'undefined' || window.localStorage === null) {
+ window.localStorage = new storage.CupcakeLocalStorage();
+ }
+
+ // Let native code know we are all done on the JS side.
+ // Native code will then un-hide the WebView.
+ channel.join(function() {
+ exec(null, null, "App", "show", []);
+ }, [channel.onCordovaReady]);
+ },
+ objects: {
+ cordova: {
+ children: {
+ JSCallback:{
+ path:"cordova/plugin/android/callback"
+ },
+ JSCallbackPolling:{
+ path:"cordova/plugin/android/polling"
+ }
+ }
+ },
+ navigator: {
+ children: {
+ app:{
+ path: "cordova/plugin/android/app"
+ }
+ }
+ },
+ File: { // exists natively on Android WebView, override
+ path: "cordova/plugin/File"
+ },
+ FileReader: { // exists natively on Android WebView, override
+ path: "cordova/plugin/FileReader"
+ },
+ FileError: { //exists natively on Android WebView on Android 4.x
+ path: "cordova/plugin/FileError"
+ },
+ MediaError: { // exists natively on Android WebView on Android 4.x
+ path: "cordova/plugin/MediaError"
+ }
+ },
+ merges: {
+ device: {
+ path: 'cordova/plugin/android/device'
+ },
+ navigator: {
+ children: {
+ notification: {
+ path: 'cordova/plugin/android/notification'
+ }
+ }
+ }
+ }
+};
+
+});
+
+// file: lib/common/plugin/Acceleration.js
+define("cordova/plugin/Acceleration", function(require, exports, module) {
+var Acceleration = function(x, y, z, timestamp) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.timestamp = timestamp || (new Date()).getTime();
+};
+
+module.exports = Acceleration;
+
+});
+
+// file: lib/common/plugin/Camera.js
+define("cordova/plugin/Camera", function(require, exports, module) {
+var exec = require('cordova/exec'),
+ Camera = require('cordova/plugin/CameraConstants');
+
+var cameraExport = {};
+
+// Tack on the Camera Constants to the base camera plugin.
+for (var key in Camera) {
+ cameraExport[key] = Camera[key];
+}
+
+/**
+ * Gets a picture from source defined by "options.sourceType", and returns the
+ * image as defined by the "options.destinationType" option.
+
+ * The defaults are sourceType=CAMERA and destinationType=FILE_URI.
+ *
+ * @param {Function} successCallback
+ * @param {Function} errorCallback
+ * @param {Object} options
+ */
+cameraExport.getPicture = function(successCallback, errorCallback, options) {
+ // successCallback required
+ if (typeof successCallback != "function") {
+ console.log("Camera Error: successCallback is not a function");
+ return;
+ }
+
+ // errorCallback optional
+ if (errorCallback && (typeof errorCallback != "function")) {
+ console.log("Camera Error: errorCallback is not a function");
+ return;
+ }
+
+ var quality = 50;
+ if (options && typeof options.quality == "number") {
+ quality = options.quality;
+ } else if (options && typeof options.quality == "string") {
+ var qlity = parseInt(options.quality, 10);
+ if (isNaN(qlity) === false) {
+ quality = qlity.valueOf();
+ }
+ }
+
+ var destinationType = Camera.DestinationType.FILE_URI;
+ if (typeof options.destinationType == "number") {
+ destinationType = options.destinationType;
+ }
+
+ var sourceType = Camera.PictureSourceType.CAMERA;
+ if (typeof options.sourceType == "number") {
+ sourceType = options.sourceType;
+ }
+
+ var targetWidth = -1;
+ if (typeof options.targetWidth == "number") {
+ targetWidth = options.targetWidth;
+ } else if (typeof options.targetWidth == "string") {
+ var width = parseInt(options.targetWidth, 10);
+ if (isNaN(width) === false) {
+ targetWidth = width.valueOf();
+ }
+ }
+
+ var targetHeight = -1;
+ if (typeof options.targetHeight == "number") {
+ targetHeight = options.targetHeight;
+ } else if (typeof options.targetHeight == "string") {
+ var height = parseInt(options.targetHeight, 10);
+ if (isNaN(height) === false) {
+ targetHeight = height.valueOf();
+ }
+ }
+
+ var encodingType = Camera.EncodingType.JPEG;
+ if (typeof options.encodingType == "number") {
+ encodingType = options.encodingType;
+ }
+
+ var mediaType = Camera.MediaType.PICTURE;
+ if (typeof options.mediaType == "number") {
+ mediaType = options.mediaType;
+ }
+ var allowEdit = false;
+ if (typeof options.allowEdit == "boolean") {
+ allowEdit = options.allowEdit;
+ } else if (typeof options.allowEdit == "number") {
+ allowEdit = options.allowEdit <= 0 ? false : true;
+ }
+ var correctOrientation = false;
+ if (typeof options.correctOrientation == "boolean") {
+ correctOrientation = options.correctOrientation;
+ } else if (typeof options.correctOrientation == "number") {
+ correctOrientation = options.correctOrientation <=0 ? false : true;
+ }
+ var saveToPhotoAlbum = false;
+ if (typeof options.saveToPhotoAlbum == "boolean") {
+ saveToPhotoAlbum = options.saveToPhotoAlbum;
+ } else if (typeof options.saveToPhotoAlbum == "number") {
+ saveToPhotoAlbum = options.saveToPhotoAlbum <=0 ? false : true;
+ }
+ var popoverOptions = null;
+ if (typeof options.popoverOptions == "object") {
+ popoverOptions = options.popoverOptions;
+ }
+
+ var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
+ mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions];
+
+ exec(successCallback, errorCallback, "Camera", "takePicture", args);
+};
+
+cameraExport.cleanup = function(successCallback, errorCallback) {
+ exec(successCallback, errorCallback, "Camera", "cleanup", []);
+};
+
+module.exports = cameraExport;
+});
+
+// file: lib/common/plugin/CameraConstants.js
+define("cordova/plugin/CameraConstants", function(require, exports, module) {
+module.exports = {
+ DestinationType:{
+ DATA_URL: 0, // Return base64 encoded string
+ FILE_URI: 1 // Return file uri (content://media/external/images/media/2 for Android)
+ },
+ EncodingType:{
+ JPEG: 0, // Return JPEG encoded image
+ PNG: 1 // Return PNG encoded image
+ },
+ MediaType:{
+ PICTURE: 0, // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType
+ VIDEO: 1, // allow selection of video only, ONLY RETURNS URL
+ ALLMEDIA : 2 // allow selection from all media types
+ },
+ PictureSourceType:{
+ PHOTOLIBRARY : 0, // Choose image from picture library (same as SAVEDPHOTOALBUM for Android)
+ CAMERA : 1, // Take picture from camera
+ SAVEDPHOTOALBUM : 2 // Choose image from picture library (same as PHOTOLIBRARY for Android)
+ },
+ PopoverArrowDirection:{
+ ARROW_UP : 1, // matches iOS UIPopoverArrowDirection constants to specify arrow location on popover
+ ARROW_DOWN : 2,
+ ARROW_LEFT : 4,
+ ARROW_RIGHT : 8,
+ ARROW_ANY : 15
+ }
+};
+});
+
+// file: lib/common/plugin/CameraPopoverOptions.js
+define("cordova/plugin/CameraPopoverOptions", function(require, exports, module) {
+var Camera = require('cordova/plugin/CameraConstants');
+
+/**
+ * Encapsulates options for iOS Popover image picker
+ */
+var CameraPopoverOptions = function(x,y,width,height,arrowDir){
+ // information of rectangle that popover should be anchored to
+ this.x = x || 0;
+ this.y = y || 32;
+ this.width = width || 320;
+ this.height = height || 480;
+ // The direction of the popover arrow
+ this.arrowDir = arrowDir || Camera.PopoverArrowDirection.ARROW_ANY;
+};
+
+module.exports = CameraPopoverOptions;
+});
+
+// file: lib/common/plugin/CaptureAudioOptions.js
+define("cordova/plugin/CaptureAudioOptions", function(require, exports, module) {
+/**
+ * Encapsulates all audio capture operation configuration options.
+ */
+var CaptureAudioOptions = function(){
+ // Upper limit of sound clips user can record. Value must be equal or greater than 1.
+ this.limit = 1;
+ // Maximum duration of a single sound clip in seconds.
+ this.duration = 0;
+ // The selected audio mode. Must match with one of the elements in supportedAudioModes array.
+ this.mode = null;
+};
+
+module.exports = CaptureAudioOptions;
+});
+
+// file: lib/common/plugin/CaptureError.js
+define("cordova/plugin/CaptureError", function(require, exports, module) {
+/**
+ * The CaptureError interface encapsulates all errors in the Capture API.
+ */
+var CaptureError = function(c) {
+ this.code = c || null;
+};
+
+// Camera or microphone failed to capture image or sound.
+CaptureError.CAPTURE_INTERNAL_ERR = 0;
+// Camera application or audio capture application is currently serving other capture request.
+CaptureError.CAPTURE_APPLICATION_BUSY = 1;
+// Invalid use of the API (e.g. limit parameter has value less than one).
+CaptureError.CAPTURE_INVALID_ARGUMENT = 2;
+// User exited camera application or audio capture application before capturing anything.
+CaptureError.CAPTURE_NO_MEDIA_FILES = 3;
+// The requested capture operation is not supported.
+CaptureError.CAPTURE_NOT_SUPPORTED = 20;
+
+module.exports = CaptureError;
+});
+
+// file: lib/common/plugin/CaptureImageOptions.js
+define("cordova/plugin/CaptureImageOptions", function(require, exports, module) {
+/**
+ * Encapsulates all image capture operation configuration options.
+ */
+var CaptureImageOptions = function(){
+ // Upper limit of images user can take. Value must be equal or greater than 1.
+ this.limit = 1;
+ // The selected image mode. Must match with one of the elements in supportedImageModes array.
+ this.mode = null;
+};
+
+module.exports = CaptureImageOptions;
+});
+
+// file: lib/common/plugin/CaptureVideoOptions.js
+define("cordova/plugin/CaptureVideoOptions", function(require, exports, module) {
+/**
+ * Encapsulates all video capture operation configuration options.
+ */
+var CaptureVideoOptions = function(){
+ // Upper limit of videos user can record. Value must be equal or greater than 1.
+ this.limit = 1;
+ // Maximum duration of a single video clip in seconds.
+ this.duration = 0;
+ // The selected video mode. Must match with one of the elements in supportedVideoModes array.
+ this.mode = null;
+};
+
+module.exports = CaptureVideoOptions;
+});
+
+// file: lib/common/plugin/CompassError.js
+define("cordova/plugin/CompassError", function(require, exports, module) {
+/**
+ * CompassError.
+ * An error code assigned by an implementation when an error has occured
+ * @constructor
+ */
+var CompassError = function(err) {
+ this.code = (err !== undefined ? err : null);
+};
+
+CompassError.COMPASS_INTERNAL_ERR = 0;
+CompassError.COMPASS_NOT_SUPPORTED = 20;
+
+module.exports = CompassError;
+});
+
+// file: lib/common/plugin/CompassHeading.js
+define("cordova/plugin/CompassHeading", function(require, exports, module) {
+var CompassHeading = function(magneticHeading, trueHeading, headingAccuracy, timestamp) {
+ this.magneticHeading = (magneticHeading !== undefined ? magneticHeading : null);
+ this.trueHeading = (trueHeading !== undefined ? trueHeading : null);
+ this.headingAccuracy = (headingAccuracy !== undefined ? headingAccuracy : null);
+ this.timestamp = (timestamp !== undefined ? timestamp : new Date().getTime());
+};
+
+module.exports = CompassHeading;
+});
+
+// file: lib/common/plugin/ConfigurationData.js
+define("cordova/plugin/ConfigurationData", function(require, exports, module) {
+/**
+ * Encapsulates a set of parameters that the capture device supports.
+ */
+function ConfigurationData() {
+ // The ASCII-encoded string in lower case representing the media type.
+ this.type = null;
+ // The height attribute represents height of the image or video in pixels.
+ // In the case of a sound clip this attribute has value 0.
+ this.height = 0;
+ // The width attribute represents width of the image or video in pixels.
+ // In the case of a sound clip this attribute has value 0
+ this.width = 0;
+}
+
+module.exports = ConfigurationData;
+});
+
+// file: lib/common/plugin/Connection.js
+define("cordova/plugin/Connection", function(require, exports, module) {
+/**
+ * Network status
+ */
+module.exports = {
+ UNKNOWN: "unknown",
+ ETHERNET: "ethernet",
+ WIFI: "wifi",
+ CELL_2G: "2g",
+ CELL_3G: "3g",
+ CELL_4G: "4g",
+ NONE: "none"
+};
+});
+
+// file: lib/common/plugin/Contact.js
+define("cordova/plugin/Contact", function(require, exports, module) {
+var exec = require('cordova/exec'),
+ ContactError = require('cordova/plugin/ContactError'),
+ utils = require('cordova/utils');
+
+/**
+* Converts primitives into Complex Object
+* Currently only used for Date fields
+*/
+function convertIn(contact) {
+ var value = contact.birthday;
+ try {
+ contact.birthday = new Date(parseFloat(value));
+ } catch (exception){
+ console.log("Cordova Contact convertIn error: exception creating date.");
+ }
+ return contact;
+}
+
+/**
+* Converts Complex objects into primitives
+* Only conversion at present is for Dates.
+**/
+
+function convertOut(contact) {
+ var value = contact.birthday;
+ if (value !== null) {
+ // try to make it a Date object if it is not already
+ if (!utils.isDate(value)){
+ try {
+ value = new Date(value);
+ } catch(exception){
+ value = null;
+ }
+ }
+ if (utils.isDate(value)){
+ value = value.valueOf(); // convert to milliseconds
+ }
+ contact.birthday = value;
+ }
+ return contact;
+}
+
+/**
+* Contains information about a single contact.
+* @constructor
+* @param {DOMString} id unique identifier
+* @param {DOMString} displayName
+* @param {ContactName} name
+* @param {DOMString} nickname
+* @param {Array.} phoneNumbers array of phone numbers
+* @param {Array.} emails array of email addresses
+* @param {Array.} addresses array of addresses
+* @param {Array.} ims instant messaging user ids
+* @param {Array.} organizations
+* @param {DOMString} birthday contact's birthday
+* @param {DOMString} note user notes about contact
+* @param {Array.} photos
+* @param {Array.} categories
+* @param {Array.} urls contact's web sites
+*/
+var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses,
+ ims, organizations, birthday, note, photos, categories, urls) {
+ this.id = id || null;
+ this.rawId = null;
+ this.displayName = displayName || null;
+ this.name = name || null; // ContactName
+ this.nickname = nickname || null;
+ this.phoneNumbers = phoneNumbers || null; // ContactField[]
+ this.emails = emails || null; // ContactField[]
+ this.addresses = addresses || null; // ContactAddress[]
+ this.ims = ims || null; // ContactField[]
+ this.organizations = organizations || null; // ContactOrganization[]
+ this.birthday = birthday || null;
+ this.note = note || null;
+ this.photos = photos || null; // ContactField[]
+ this.categories = categories || null; // ContactField[]
+ this.urls = urls || null; // ContactField[]
+};
+
+/**
+* Removes contact from device storage.
+* @param successCB success callback
+* @param errorCB error callback
+*/
+Contact.prototype.remove = function(successCB, errorCB) {
+ var fail = function(code) {
+ errorCB(new ContactError(code));
+ };
+ if (this.id === null) {
+ fail(ContactError.UNKNOWN_ERROR);
+ }
+ else {
+ exec(successCB, fail, "Contacts", "remove", [this.id]);
+ }
+};
+
+/**
+* Creates a deep copy of this Contact.
+* With the contact ID set to null.
+* @return copy of this Contact
+*/
+Contact.prototype.clone = function() {
+ var clonedContact = utils.clone(this);
+ var i;
+ clonedContact.id = null;
+ clonedContact.rawId = null;
+ // Loop through and clear out any id's in phones, emails, etc.
+ if (clonedContact.phoneNumbers) {
+ for (i = 0; i < clonedContact.phoneNumbers.length; i++) {
+ clonedContact.phoneNumbers[i].id = null;
+ }
+ }
+ if (clonedContact.emails) {
+ for (i = 0; i < clonedContact.emails.length; i++) {
+ clonedContact.emails[i].id = null;
+ }
+ }
+ if (clonedContact.addresses) {
+ for (i = 0; i < clonedContact.addresses.length; i++) {
+ clonedContact.addresses[i].id = null;
+ }
+ }
+ if (clonedContact.ims) {
+ for (i = 0; i < clonedContact.ims.length; i++) {
+ clonedContact.ims[i].id = null;
+ }
+ }
+ if (clonedContact.organizations) {
+ for (i = 0; i < clonedContact.organizations.length; i++) {
+ clonedContact.organizations[i].id = null;
+ }
+ }
+ if (clonedContact.categories) {
+ for (i = 0; i < clonedContact.categories.length; i++) {
+ clonedContact.categories[i].id = null;
+ }
+ }
+ if (clonedContact.photos) {
+ for (i = 0; i < clonedContact.photos.length; i++) {
+ clonedContact.photos[i].id = null;
+ }
+ }
+ if (clonedContact.urls) {
+ for (i = 0; i < clonedContact.urls.length; i++) {
+ clonedContact.urls[i].id = null;
+ }
+ }
+ return clonedContact;
+};
+
+/**
+* Persists contact to device storage.
+* @param successCB success callback
+* @param errorCB error callback
+*/
+Contact.prototype.save = function(successCB, errorCB) {
+ var fail = function(code) {
+ errorCB(new ContactError(code));
+ };
+ var success = function(result) {
+ if (result) {
+ if (typeof successCB === 'function') {
+ var fullContact = require('cordova/plugin/contacts').create(result);
+ successCB(convertIn(fullContact));
+ }
+ }
+ else {
+ // no Entry object returned
+ fail(ContactError.UNKNOWN_ERROR);
+ }
+ };
+ var dupContact = convertOut(utils.clone(this));
+ exec(success, fail, "Contacts", "save", [dupContact]);
+};
+
+
+module.exports = Contact;
+
+});
+
+// file: lib/common/plugin/ContactAddress.js
+define("cordova/plugin/ContactAddress", function(require, exports, module) {
+/**
+* Contact address.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code
+* @param formatted // NOTE: not a W3C standard
+* @param streetAddress
+* @param locality
+* @param region
+* @param postalCode
+* @param country
+*/
+
+var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) {
+ this.id = null;
+ this.pref = (typeof pref != 'undefined' ? pref : false);
+ this.type = type || null;
+ this.formatted = formatted || null;
+ this.streetAddress = streetAddress || null;
+ this.locality = locality || null;
+ this.region = region || null;
+ this.postalCode = postalCode || null;
+ this.country = country || null;
+};
+
+module.exports = ContactAddress;
+});
+
+// file: lib/common/plugin/ContactError.js
+define("cordova/plugin/ContactError", function(require, exports, module) {
+/**
+ * ContactError.
+ * An error code assigned by an implementation when an error has occured
+ * @constructor
+ */
+var ContactError = function(err) {
+ this.code = (typeof err != 'undefined' ? err : null);
+};
+
+/**
+ * Error codes
+ */
+ContactError.UNKNOWN_ERROR = 0;
+ContactError.INVALID_ARGUMENT_ERROR = 1;
+ContactError.TIMEOUT_ERROR = 2;
+ContactError.PENDING_OPERATION_ERROR = 3;
+ContactError.IO_ERROR = 4;
+ContactError.NOT_SUPPORTED_ERROR = 5;
+ContactError.PERMISSION_DENIED_ERROR = 20;
+
+module.exports = ContactError;
+});
+
+// file: lib/common/plugin/ContactField.js
+define("cordova/plugin/ContactField", function(require, exports, module) {
+/**
+* Generic contact field.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard
+* @param type
+* @param value
+* @param pref
+*/
+var ContactField = function(type, value, pref) {
+ this.id = null;
+ this.type = (type && type.toString()) || null;
+ this.value = (value && value.toString()) || null;
+ this.pref = (typeof pref != 'undefined' ? pref : false);
+};
+
+module.exports = ContactField;
+});
+
+// file: lib/common/plugin/ContactFindOptions.js
+define("cordova/plugin/ContactFindOptions", function(require, exports, module) {
+/**
+ * ContactFindOptions.
+ * @constructor
+ * @param filter used to match contacts against
+ * @param multiple boolean used to determine if more than one contact should be returned
+ */
+
+var ContactFindOptions = function(filter, multiple) {
+ this.filter = filter || '';
+ this.multiple = (typeof multiple != 'undefined' ? multiple : false);
+};
+
+module.exports = ContactFindOptions;
+});
+
+// file: lib/common/plugin/ContactName.js
+define("cordova/plugin/ContactName", function(require, exports, module) {
+/**
+* Contact name.
+* @constructor
+* @param formatted // NOTE: not part of W3C standard
+* @param familyName
+* @param givenName
+* @param middle
+* @param prefix
+* @param suffix
+*/
+var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) {
+ this.formatted = formatted || null;
+ this.familyName = familyName || null;
+ this.givenName = givenName || null;
+ this.middleName = middle || null;
+ this.honorificPrefix = prefix || null;
+ this.honorificSuffix = suffix || null;
+};
+
+module.exports = ContactName;
+});
+
+// file: lib/common/plugin/ContactOrganization.js
+define("cordova/plugin/ContactOrganization", function(require, exports, module) {
+/**
+* Contact organization.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard
+* @param name
+* @param dept
+* @param title
+* @param startDate
+* @param endDate
+* @param location
+* @param desc
+*/
+
+var ContactOrganization = function(pref, type, name, dept, title) {
+ this.id = null;
+ this.pref = (typeof pref != 'undefined' ? pref : false);
+ this.type = type || null;
+ this.name = name || null;
+ this.department = dept || null;
+ this.title = title || null;
+};
+
+module.exports = ContactOrganization;
+});
+
+// file: lib/common/plugin/Coordinates.js
+define("cordova/plugin/Coordinates", function(require, exports, module) {
+/**
+ * This class contains position information.
+ * @param {Object} lat
+ * @param {Object} lng
+ * @param {Object} alt
+ * @param {Object} acc
+ * @param {Object} head
+ * @param {Object} vel
+ * @param {Object} altacc
+ * @constructor
+ */
+var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) {
+ /**
+ * The latitude of the position.
+ */
+ this.latitude = lat;
+ /**
+ * The longitude of the position,
+ */
+ this.longitude = lng;
+ /**
+ * The accuracy of the position.
+ */
+ this.accuracy = acc;
+ /**
+ * The altitude of the position.
+ */
+ this.altitude = (alt !== undefined ? alt : null);
+ /**
+ * The direction the device is moving at the position.
+ */
+ this.heading = (head !== undefined ? head : null);
+ /**
+ * The velocity with which the device is moving at the position.
+ */
+ this.speed = (vel !== undefined ? vel : null);
+
+ if (this.speed === 0 || this.speed === null) {
+ this.heading = NaN;
+ }
+
+ /**
+ * The altitude accuracy of the position.
+ */
+ this.altitudeAccuracy = (altacc !== undefined) ? altacc : null;
+};
+
+module.exports = Coordinates;
+
+});
+
+// file: lib/common/plugin/DirectoryEntry.js
+define("cordova/plugin/DirectoryEntry", function(require, exports, module) {
+var utils = require('cordova/utils'),
+ exec = require('cordova/exec'),
+ Entry = require('cordova/plugin/Entry'),
+ FileError = require('cordova/plugin/FileError'),
+ DirectoryReader = require('cordova/plugin/DirectoryReader');
+
+/**
+ * An interface representing a directory on the file system.
+ *
+ * {boolean} isFile always false (readonly)
+ * {boolean} isDirectory always true (readonly)
+ * {DOMString} name of the directory, excluding the path leading to it (readonly)
+ * {DOMString} fullPath the absolute full path to the directory (readonly)
+ * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly)
+ */
+var DirectoryEntry = function(name, fullPath) {
+ DirectoryEntry.__super__.constructor.apply(this, [false, true, name, fullPath]);
+};
+
+utils.extend(DirectoryEntry, Entry);
+
+/**
+ * Creates a new DirectoryReader to read entries from this directory
+ */
+DirectoryEntry.prototype.createReader = function() {
+ return new DirectoryReader(this.fullPath);
+};
+
+/**
+ * Creates or looks up a directory
+ *
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory
+ * @param {Flags} options to create or excluively create the directory
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) {
+ var win = typeof successCallback !== 'function' ? null : function(result) {
+ var entry = new DirectoryEntry(result.name, result.fullPath);
+ successCallback(entry);
+ };
+ var fail = typeof errorCallback !== 'function' ? null : function(code) {
+ errorCallback(new FileError(code));
+ };
+ exec(win, fail, "File", "getDirectory", [this.fullPath, path, options]);
+};
+
+/**
+ * Deletes a directory and all of it's contents
+ *
+ * @param {Function} successCallback is called with no parameters
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) {
+ var fail = typeof errorCallback !== 'function' ? null : function(code) {
+ errorCallback(new FileError(code));
+ };
+ exec(successCallback, fail, "File", "removeRecursively", [this.fullPath]);
+};
+
+/**
+ * Creates or looks up a file
+ *
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file
+ * @param {Flags} options to create or excluively create the file
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) {
+ var win = typeof successCallback !== 'function' ? null : function(result) {
+ var FileEntry = require('cordova/plugin/FileEntry');
+ var entry = new FileEntry(result.name, result.fullPath);
+ successCallback(entry);
+ };
+ var fail = typeof errorCallback !== 'function' ? null : function(code) {
+ errorCallback(new FileError(code));
+ };
+ exec(win, fail, "File", "getFile", [this.fullPath, path, options]);
+};
+
+module.exports = DirectoryEntry;
+
+});
+
+// file: lib/common/plugin/DirectoryReader.js
+define("cordova/plugin/DirectoryReader", function(require, exports, module) {
+var exec = require('cordova/exec'),
+ FileError = require('cordova/plugin/FileError') ;
+
+/**
+ * An interface that lists the files and directories in a directory.
+ */
+function DirectoryReader(path) {
+ this.path = path || null;
+}
+
+/**
+ * Returns a list of entries from a directory.
+ *
+ * @param {Function} successCallback is called with a list of entries
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) {
+ var win = typeof successCallback !== 'function' ? null : function(result) {
+ var retVal = [];
+ for (var i=0; i][;base64],
+ *
+ * @param file {File} File object containing file properties
+ */
+FileReader.prototype.readAsDataURL = function(file) {
+ this.fileName = "";
+ if (typeof file.fullPath === "undefined") {
+ this.fileName = file;
+ } else {
+ this.fileName = file.fullPath;
+ }
+
+ // Already loading something
+ if (this.readyState == FileReader.LOADING) {
+ throw new FileError(FileError.INVALID_STATE_ERR);
+ }
+
+ // LOADING state
+ this.readyState = FileReader.LOADING;
+
+ // If loadstart callback
+ if (typeof this.onloadstart === "function") {
+ this.onloadstart(new ProgressEvent("loadstart", {target:this}));
+ }
+
+ var me = this;
+
+ // Read file
+ exec(
+ // Success callback
+ function(r) {
+ // If DONE (cancelled), then don't do anything
+ if (me.readyState === FileReader.DONE) {
+ return;
+ }
+
+ // DONE state
+ me.readyState = FileReader.DONE;
+
+ // Save result
+ me.result = r;
+
+ // If onload callback
+ if (typeof me.onload === "function") {
+ me.onload(new ProgressEvent("load", {target:me}));
+ }
+
+ // If onloadend callback
+ if (typeof me.onloadend === "function") {
+ me.onloadend(new ProgressEvent("loadend", {target:me}));
+ }
+ },
+ // Error callback
+ function(e) {
+ // If DONE (cancelled), then don't do anything
+ if (me.readyState === FileReader.DONE) {
+ return;
+ }
+
+ // DONE state
+ me.readyState = FileReader.DONE;
+
+ me.result = null;
+
+ // Save error
+ me.error = new FileError(e);
+
+ // If onerror callback
+ if (typeof me.onerror === "function") {
+ me.onerror(new ProgressEvent("error", {target:me}));
+ }
+
+ // If onloadend callback
+ if (typeof me.onloadend === "function") {
+ me.onloadend(new ProgressEvent("loadend", {target:me}));
+ }
+ }, "File", "readAsDataURL", [this.fileName]);
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file {File} File object containing file properties
+ */
+FileReader.prototype.readAsBinaryString = function(file) {
+ // TODO - Can't return binary data to browser.
+ console.log('method "readAsBinaryString" is not supported at this time.');
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file {File} File object containing file properties
+ */
+FileReader.prototype.readAsArrayBuffer = function(file) {
+ // TODO - Can't return binary data to browser.
+ console.log('This method is not supported at this time.');
+};
+
+module.exports = FileReader;
+});
+
+// file: lib/common/plugin/FileSystem.js
+define("cordova/plugin/FileSystem", function(require, exports, module) {
+var DirectoryEntry = require('cordova/plugin/DirectoryEntry');
+
+/**
+ * An interface representing a file system
+ *
+ * @constructor
+ * {DOMString} name the unique name of the file system (readonly)
+ * {DirectoryEntry} root directory of the file system (readonly)
+ */
+var FileSystem = function(name, root) {
+ this.name = name || null;
+ if (root) {
+ this.root = new DirectoryEntry(root.name, root.fullPath);
+ }
+};
+
+module.exports = FileSystem;
+
+});
+
+// file: lib/common/plugin/FileTransfer.js
+define("cordova/plugin/FileTransfer", function(require, exports, module) {
+var exec = require('cordova/exec'),
+ FileTransferError = require('cordova/plugin/FileTransferError');
+
+/**
+ * FileTransfer uploads a file to a remote server.
+ * @constructor
+ */
+var FileTransfer = function() {};
+
+/**
+* Given an absolute file path, uploads a file on the device to a remote server
+* using a multipart HTTP request.
+* @param filePath {String} Full path of the file on the device
+* @param server {String} URL of the server to receive the file
+* @param successCallback (Function} Callback to be invoked when upload has completed
+* @param errorCallback {Function} Callback to be invoked upon error
+* @param options {FileUploadOptions} Optional parameters such as file name and mimetype
+* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
+*/
+FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) {
+ // sanity parameter checking
+ if (!filePath || !server) throw new Error("FileTransfer.upload requires filePath and server URL parameters at the minimum.");
+ // check for options
+ var fileKey = null;
+ var fileName = null;
+ var mimeType = null;
+ var params = null;
+ var chunkedMode = true;
+ if (options) {
+ fileKey = options.fileKey;
+ fileName = options.fileName;
+ mimeType = options.mimeType;
+ if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") {
+ chunkedMode = options.chunkedMode;
+ }
+ if (options.params) {
+ params = options.params;
+ }
+ else {
+ params = {};
+ }
+ }
+
+ var fail = function(e) {
+ var error = new FileTransferError(e.code, e.source, e.target, e.http_status);
+ errorCallback(error);
+ };
+
+ exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode]);
+};
+
+/**
+ * Downloads a file form a given URL and saves it to the specified directory.
+ * @param source {String} URL of the server to receive the file
+ * @param target {String} Full path of the file on the device
+ * @param successCallback (Function} Callback to be invoked when upload has completed
+ * @param errorCallback {Function} Callback to be invoked upon error
+ */
+FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) {
+ // sanity parameter checking
+ if (!source || !target) throw new Error("FileTransfer.download requires source URI and target URI parameters at the minimum.");
+ var win = function(result) {
+ var entry = null;
+ if (result.isDirectory) {
+ entry = new (require('cordova/plugin/DirectoryEntry'))();
+ }
+ else if (result.isFile) {
+ entry = new (require('cordova/plugin/FileEntry'))();
+ }
+ entry.isDirectory = result.isDirectory;
+ entry.isFile = result.isFile;
+ entry.name = result.name;
+ entry.fullPath = result.fullPath;
+ successCallback(entry);
+ };
+
+ var fail = function(e) {
+ var error = new FileTransferError(e.code, e.source, e.target, e.http_status);
+ errorCallback(error);
+ };
+
+ exec(win, errorCallback, 'FileTransfer', 'download', [source, target]);
+};
+
+module.exports = FileTransfer;
+
+});
+
+// file: lib/common/plugin/FileTransferError.js
+define("cordova/plugin/FileTransferError", function(require, exports, module) {
+/**
+ * FileTransferError
+ * @constructor
+ */
+var FileTransferError = function(code, source, target, status) {
+ this.code = code || null;
+ this.source = source || null;
+ this.target = target || null;
+ this.http_status = status || null;
+};
+
+FileTransferError.FILE_NOT_FOUND_ERR = 1;
+FileTransferError.INVALID_URL_ERR = 2;
+FileTransferError.CONNECTION_ERR = 3;
+
+module.exports = FileTransferError;
+
+});
+
+// file: lib/common/plugin/FileUploadOptions.js
+define("cordova/plugin/FileUploadOptions", function(require, exports, module) {
+/**
+ * Options to customize the HTTP request used to upload files.
+ * @constructor
+ * @param fileKey {String} Name of file request parameter.
+ * @param fileName {String} Filename to be used by the server. Defaults to image.jpg.
+ * @param mimeType {String} Mimetype of the uploaded file. Defaults to image/jpeg.
+ * @param params {Object} Object with key: value params to send to the server.
+ */
+var FileUploadOptions = function(fileKey, fileName, mimeType, params) {
+ this.fileKey = fileKey || null;
+ this.fileName = fileName || null;
+ this.mimeType = mimeType || null;
+ this.params = params || null;
+};
+
+module.exports = FileUploadOptions;
+});
+
+// file: lib/common/plugin/FileUploadResult.js
+define("cordova/plugin/FileUploadResult", function(require, exports, module) {
+/**
+ * FileUploadResult
+ * @constructor
+ */
+var FileUploadResult = function() {
+ this.bytesSent = 0;
+ this.responseCode = null;
+ this.response = null;
+};
+
+module.exports = FileUploadResult;
+});
+
+// file: lib/common/plugin/FileWriter.js
+define("cordova/plugin/FileWriter", function(require, exports, module) {
+var exec = require('cordova/exec'),
+ FileError = require('cordova/plugin/FileError'),
+ ProgressEvent = require('cordova/plugin/ProgressEvent');
+
+/**
+ * This class writes to the mobile device file system.
+ *
+ * For Android:
+ * The root directory is the root of the file system.
+ * To write to the SD card, the file name is "sdcard/my_file.txt"
+ *
+ * @constructor
+ * @param file {File} File object containing file properties
+ * @param append if true write to the end of the file, otherwise overwrite the file
+ */
+var FileWriter = function(file) {
+ this.fileName = "";
+ this.length = 0;
+ if (file) {
+ this.fileName = file.fullPath || file;
+ this.length = file.size || 0;
+ }
+ // default is to write at the beginning of the file
+ this.position = 0;
+
+ this.readyState = 0; // EMPTY
+
+ this.result = null;
+
+ // Error
+ this.error = null;
+
+ // Event handlers
+ this.onwritestart = null; // When writing starts
+ this.onprogress = null; // While writing the file, and reporting partial file data
+ this.onwrite = null; // When the write has successfully completed.
+ this.onwriteend = null; // When the request has completed (either in success or failure).
+ this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method.
+ this.onerror = null; // When the write has failed (see errors).
+};
+
+// States
+FileWriter.INIT = 0;
+FileWriter.WRITING = 1;
+FileWriter.DONE = 2;
+
+/**
+ * Abort writing file.
+ */
+FileWriter.prototype.abort = function() {
+ // check for invalid state
+ if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) {
+ throw new FileError(FileError.INVALID_STATE_ERR);
+ }
+
+ // set error
+ this.error = new FileError(FileError.ABORT_ERR);
+
+ this.readyState = FileWriter.DONE;
+
+ // If abort callback
+ if (typeof this.onabort === "function") {
+ this.onabort(new ProgressEvent("abort", {"target":this}));
+ }
+
+ // If write end callback
+ if (typeof this.onwriteend === "function") {
+ this.onwriteend(new ProgressEvent("writeend", {"target":this}));
+ }
+};
+
+/**
+ * Writes data to the file
+ *
+ * @param text to be written
+ */
+FileWriter.prototype.write = function(text) {
+ // Throw an exception if we are already writing a file
+ if (this.readyState === FileWriter.WRITING) {
+ throw new FileError(FileError.INVALID_STATE_ERR);
+ }
+
+ // WRITING state
+ this.readyState = FileWriter.WRITING;
+
+ var me = this;
+
+ // If onwritestart callback
+ if (typeof me.onwritestart === "function") {
+ me.onwritestart(new ProgressEvent("writestart", {"target":me}));
+ }
+
+ // Write file
+ exec(
+ // Success callback
+ function(r) {
+ // If DONE (cancelled), then don't do anything
+ if (me.readyState === FileWriter.DONE) {
+ return;
+ }
+
+ // position always increases by bytes written because file would be extended
+ me.position += r;
+ // The length of the file is now where we are done writing.
+
+ me.length = me.position;
+
+ // DONE state
+ me.readyState = FileWriter.DONE;
+
+ // If onwrite callback
+ if (typeof me.onwrite === "function") {
+ me.onwrite(new ProgressEvent("write", {"target":me}));
+ }
+
+ // If onwriteend callback
+ if (typeof me.onwriteend === "function") {
+ me.onwriteend(new ProgressEvent("writeend", {"target":me}));
+ }
+ },
+ // Error callback
+ function(e) {
+ // If DONE (cancelled), then don't do anything
+ if (me.readyState === FileWriter.DONE) {
+ return;
+ }
+
+ // DONE state
+ me.readyState = FileWriter.DONE;
+
+ // Save error
+ me.error = new FileError(e);
+
+ // If onerror callback
+ if (typeof me.onerror === "function") {
+ me.onerror(new ProgressEvent("error", {"target":me}));
+ }
+
+ // If onwriteend callback
+ if (typeof me.onwriteend === "function") {
+ me.onwriteend(new ProgressEvent("writeend", {"target":me}));
+ }
+ }, "File", "write", [this.fileName, text, this.position]);
+};
+
+/**
+ * Moves the file pointer to the location specified.
+ *
+ * If the offset is a negative number the position of the file
+ * pointer is rewound. If the offset is greater than the file
+ * size the position is set to the end of the file.
+ *
+ * @param offset is the location to move the file pointer to.
+ */
+FileWriter.prototype.seek = function(offset) {
+ // Throw an exception if we are already writing a file
+ if (this.readyState === FileWriter.WRITING) {
+ throw new FileError(FileError.INVALID_STATE_ERR);
+ }
+
+ if (!offset && offset !== 0) {
+ return;
+ }
+
+ // See back from end of file.
+ if (offset < 0) {
+ this.position = Math.max(offset + this.length, 0);
+ }
+ // Offset is bigger then file size so set position
+ // to the end of the file.
+ else if (offset > this.length) {
+ this.position = this.length;
+ }
+ // Offset is between 0 and file size so set the position
+ // to start writing.
+ else {
+ this.position = offset;
+ }
+};
+
+/**
+ * Truncates the file to the size specified.
+ *
+ * @param size to chop the file at.
+ */
+FileWriter.prototype.truncate = function(size) {
+ // Throw an exception if we are already writing a file
+ if (this.readyState === FileWriter.WRITING) {
+ throw new FileError(FileError.INVALID_STATE_ERR);
+ }
+
+ // WRITING state
+ this.readyState = FileWriter.WRITING;
+
+ var me = this;
+
+ // If onwritestart callback
+ if (typeof me.onwritestart === "function") {
+ me.onwritestart(new ProgressEvent("writestart", {"target":this}));
+ }
+
+ // Write file
+ exec(
+ // Success callback
+ function(r) {
+ // If DONE (cancelled), then don't do anything
+ if (me.readyState === FileWriter.DONE) {
+ return;
+ }
+
+ // DONE state
+ me.readyState = FileWriter.DONE;
+
+ // Update the length of the file
+ me.length = r;
+ me.position = Math.min(me.position, r);
+
+ // If onwrite callback
+ if (typeof me.onwrite === "function") {
+ me.onwrite(new ProgressEvent("write", {"target":me}));
+ }
+
+ // If onwriteend callback
+ if (typeof me.onwriteend === "function") {
+ me.onwriteend(new ProgressEvent("writeend", {"target":me}));
+ }
+ },
+ // Error callback
+ function(e) {
+ // If DONE (cancelled), then don't do anything
+ if (me.readyState === FileWriter.DONE) {
+ return;
+ }
+
+ // DONE state
+ me.readyState = FileWriter.DONE;
+
+ // Save error
+ me.error = new FileError(e);
+
+ // If onerror callback
+ if (typeof me.onerror === "function") {
+ me.onerror(new ProgressEvent("error", {"target":me}));
+ }
+
+ // If onwriteend callback
+ if (typeof me.onwriteend === "function") {
+ me.onwriteend(new ProgressEvent("writeend", {"target":me}));
+ }
+ }, "File", "truncate", [this.fileName, size]);
+};
+
+module.exports = FileWriter;
+
+});
+
+// file: lib/common/plugin/Flags.js
+define("cordova/plugin/Flags", function(require, exports, module) {
+/**
+ * Supplies arguments to methods that lookup or create files and directories.
+ *
+ * @param create
+ * {boolean} file or directory if it doesn't exist
+ * @param exclusive
+ * {boolean} used with create; if true the command will fail if
+ * target path exists
+ */
+function Flags(create, exclusive) {
+ this.create = create || false;
+ this.exclusive = exclusive || false;
+}
+
+module.exports = Flags;
+});
+
+// file: lib/common/plugin/LocalFileSystem.js
+define("cordova/plugin/LocalFileSystem", function(require, exports, module) {
+var exec = require('cordova/exec');
+
+/**
+ * Represents a local file system.
+ */
+var LocalFileSystem = function() {
+
+};
+
+LocalFileSystem.TEMPORARY = 0; //temporary, with no guarantee of persistence
+LocalFileSystem.PERSISTENT = 1; //persistent
+
+module.exports = LocalFileSystem;
+});
+
+// file: lib/common/plugin/Media.js
+define("cordova/plugin/Media", function(require, exports, module) {
+var utils = require('cordova/utils'),
+ exec = require('cordova/exec');
+
+var mediaObjects = {};
+
+/**
+ * This class provides access to the device media, interfaces to both sound and video
+ *
+ * @constructor
+ * @param src The file name or url to play
+ * @param successCallback The callback to be called when the file is done playing or recording.
+ * successCallback()
+ * @param errorCallback The callback to be called if there is an error.
+ * errorCallback(int errorCode) - OPTIONAL
+ * @param statusCallback The callback to be called when media status has changed.
+ * statusCallback(int statusCode) - OPTIONAL
+ */
+var Media = function(src, successCallback, errorCallback, statusCallback) {
+
+ // successCallback optional
+ if (successCallback && (typeof successCallback !== "function")) {
+ console.log("Media Error: successCallback is not a function");
+ return;
+ }
+
+ // errorCallback optional
+ if (errorCallback && (typeof errorCallback !== "function")) {
+ console.log("Media Error: errorCallback is not a function");
+ return;
+ }
+
+ // statusCallback optional
+ if (statusCallback && (typeof statusCallback !== "function")) {
+ console.log("Media Error: statusCallback is not a function");
+ return;
+ }
+
+ this.id = utils.createUUID();
+ mediaObjects[this.id] = this;
+ this.src = src;
+ this.successCallback = successCallback;
+ this.errorCallback = errorCallback;
+ this.statusCallback = statusCallback;
+ this._duration = -1;
+ this._position = -1;
+ exec(null, this.errorCallback, "Media", "create", [this.id, this.src]);
+};
+
+// Media messages
+Media.MEDIA_STATE = 1;
+Media.MEDIA_DURATION = 2;
+Media.MEDIA_POSITION = 3;
+Media.MEDIA_ERROR = 9;
+
+// Media states
+Media.MEDIA_NONE = 0;
+Media.MEDIA_STARTING = 1;
+Media.MEDIA_RUNNING = 2;
+Media.MEDIA_PAUSED = 3;
+Media.MEDIA_STOPPED = 4;
+Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"];
+
+// "static" function to return existing objs.
+Media.get = function(id) {
+ return mediaObjects[id];
+};
+
+/**
+ * Start or resume playing audio file.
+ */
+Media.prototype.play = function(options) {
+ exec(null, null, "Media", "startPlayingAudio", [this.id, this.src, options]);
+};
+
+/**
+ * Stop playing audio file.
+ */
+Media.prototype.stop = function() {
+ var me = this;
+ exec(function() {
+ me._position = 0;
+ me.successCallback();
+ }, this.errorCallback, "Media", "stopPlayingAudio", [this.id]);
+};
+
+/**
+ * Seek or jump to a new time in the track..
+ */
+Media.prototype.seekTo = function(milliseconds) {
+ var me = this;
+ exec(function(p) {
+ me._position = p;
+ }, this.errorCallback, "Media", "seekToAudio", [this.id, milliseconds]);
+};
+
+/**
+ * Pause playing audio file.
+ */
+Media.prototype.pause = function() {
+ exec(null, this.errorCallback, "Media", "pausePlayingAudio", [this.id]);
+};
+
+/**
+ * Get duration of an audio file.
+ * The duration is only set for audio that is playing, paused or stopped.
+ *
+ * @return duration or -1 if not known.
+ */
+Media.prototype.getDuration = function() {
+ return this._duration;
+};
+
+/**
+ * Get position of audio.
+ */
+Media.prototype.getCurrentPosition = function(success, fail) {
+ var me = this;
+ exec(function(p) {
+ me._position = p;
+ success(p);
+ }, fail, "Media", "getCurrentPositionAudio", [this.id]);
+};
+
+/**
+ * Start recording audio file.
+ */
+Media.prototype.startRecord = function() {
+ exec(this.successCallback, this.errorCallback, "Media", "startRecordingAudio", [this.id, this.src]);
+};
+
+/**
+ * Stop recording audio file.
+ */
+Media.prototype.stopRecord = function() {
+ exec(this.successCallback, this.errorCallback, "Media", "stopRecordingAudio", [this.id]);
+};
+
+/**
+ * Release the resources.
+ */
+Media.prototype.release = function() {
+ exec(null, this.errorCallback, "Media", "release", [this.id]);
+};
+
+/**
+ * Adjust the volume.
+ */
+Media.prototype.setVolume = function(volume) {
+ exec(null, null, "Media", "setVolume", [this.id, volume]);
+};
+
+/**
+ * Audio has status update.
+ * PRIVATE
+ *
+ * @param id The media object id (string)
+ * @param status The status code (int)
+ * @param msg The status message (string)
+ */
+Media.onStatus = function(id, msg, value) {
+ var media = mediaObjects[id];
+ // If state update
+ if (msg === Media.MEDIA_STATE) {
+ if (value === Media.MEDIA_STOPPED) {
+ if (media.successCallback) {
+ media.successCallback();
+ }
+ }
+ if (media.statusCallback) {
+ media.statusCallback(value);
+ }
+ }
+ else if (msg === Media.MEDIA_DURATION) {
+ media._duration = value;
+ }
+ else if (msg === Media.MEDIA_ERROR) {
+ if (media.errorCallback) {
+ // value should be a MediaError object when msg == MEDIA_ERROR
+ media.errorCallback(value);
+ }
+ }
+ else if (msg === Media.MEDIA_POSITION) {
+ media._position = value;
+ }
+};
+
+module.exports = Media;
+});
+
+// file: lib/common/plugin/MediaError.js
+define("cordova/plugin/MediaError", function(require, exports, module) {
+/**
+ * This class contains information about any Media errors.
+ * @constructor
+ */
+var MediaError = function(code, msg) {
+ this.code = (code !== undefined ? code : null);
+ this.message = msg || "";
+};
+
+MediaError.MEDIA_ERR_NONE_ACTIVE = 0;
+MediaError.MEDIA_ERR_ABORTED = 1;
+MediaError.MEDIA_ERR_NETWORK = 2;
+MediaError.MEDIA_ERR_DECODE = 3;
+MediaError.MEDIA_ERR_NONE_SUPPORTED = 4;
+
+module.exports = MediaError;
+});
+
+// file: lib/common/plugin/MediaFile.js
+define("cordova/plugin/MediaFile", function(require, exports, module) {
+var utils = require('cordova/utils'),
+ exec = require('cordova/exec'),
+ File = require('cordova/plugin/File'),
+ CaptureError = require('cordova/plugin/CaptureError');
+/**
+ * Represents a single file.
+ *
+ * name {DOMString} name of the file, without path information
+ * fullPath {DOMString} the full path of the file, including the name
+ * type {DOMString} mime type
+ * lastModifiedDate {Date} last modified date
+ * size {Number} size of the file in bytes
+ */
+var MediaFile = function(name, fullPath, type, lastModifiedDate, size){
+ MediaFile.__super__.constructor.apply(this, arguments);
+};
+
+utils.extend(MediaFile, File);
+
+/**
+ * Request capture format data for a specific file and type
+ *
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ */
+MediaFile.prototype.getFormatData = function(successCallback, errorCallback) {
+ if (typeof this.fullPath === "undefined" || this.fullPath === null) {
+ errorCallback(new CaptureError(CaptureError.CAPTURE_INVALID_ARGUMENT));
+ } else {
+ exec(successCallback, errorCallback, "Capture", "getFormatData", [this.fullPath, this.type]);
+ }
+};
+
+// TODO: can we axe this?
+/**
+ * Casts a PluginResult message property (array of objects) to an array of MediaFile objects
+ * (used in Objective-C and Android)
+ *
+ * @param {PluginResult} pluginResult
+ */
+MediaFile.cast = function(pluginResult) {
+ var mediaFiles = [];
+ for (var i=0; i.dispatchEvent
+ // need to first figure out how to implement EventTarget
+ }
+ }
+ return event;
+ };
+ try {
+ var ev = createEvent({type:"abort",target:document});
+ return function ProgressEvent(type, data) {
+ data.type = type;
+ return createEvent(data);
+ };
+ } catch(e){
+ */
+ return function ProgressEvent(type, dict) {
+ this.type = type;
+ this.bubbles = false;
+ this.cancelBubble = false;
+ this.cancelable = false;
+ this.lengthComputable = false;
+ this.loaded = dict && dict.loaded ? dict.loaded : 0;
+ this.total = dict && dict.total ? dict.total : 0;
+ this.target = dict && dict.target ? dict.target : null;
+ };
+ //}
+})();
+
+module.exports = ProgressEvent;
+});
+
+// file: lib/common/plugin/accelerometer.js
+define("cordova/plugin/accelerometer", function(require, exports, module) {
+/**
+ * This class provides access to device accelerometer data.
+ * @constructor
+ */
+var utils = require("cordova/utils"),
+ exec = require("cordova/exec"),
+ Acceleration = require('cordova/plugin/Acceleration');
+
+// Is the accel sensor running?
+var running = false;
+
+// Keeps reference to watchAcceleration calls.
+var timers = {};
+
+// Array of listeners; used to keep track of when we should call start and stop.
+var listeners = [];
+
+// Last returned acceleration object from native
+var accel = null;
+
+// Tells native to start.
+function start() {
+ exec(function(a) {
+ var tempListeners = listeners.slice(0);
+ accel = new Acceleration(a.x, a.y, a.z, a.timestamp);
+ for (var i = 0, l = tempListeners.length; i < l; i++) {
+ tempListeners[i].win(accel);
+ }
+ }, function(e) {
+ var tempListeners = listeners.slice(0);
+ for (var i = 0, l = tempListeners.length; i < l; i++) {
+ tempListeners[i].fail(e);
+ }
+ }, "Accelerometer", "start", []);
+ running = true;
+}
+
+// Tells native to stop.
+function stop() {
+ exec(null, null, "Accelerometer", "stop", []);
+ running = false;
+}
+
+// Adds a callback pair to the listeners array
+function createCallbackPair(win, fail) {
+ return {win:win, fail:fail};
+}
+
+// Removes a win/fail listener pair from the listeners array
+function removeListeners(l) {
+ var idx = listeners.indexOf(l);
+ if (idx > -1) {
+ listeners.splice(idx, 1);
+ if (listeners.length === 0) {
+ stop();
+ }
+ }
+}
+
+var accelerometer = {
+ /**
+ * Asynchronously aquires the current acceleration.
+ *
+ * @param {Function} successCallback The function to call when the acceleration data is available
+ * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL)
+ * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL)
+ */
+ getCurrentAcceleration: function(successCallback, errorCallback, options) {
+ // successCallback required
+ if (typeof successCallback !== "function") {
+ throw "getCurrentAcceleration must be called with at least a success callback function as first parameter.";
+ }
+
+ var p;
+ var win = function(a) {
+ removeListeners(p);
+ successCallback(a);
+ };
+ var fail = function(e) {
+ removeListeners(p);
+ errorCallback(e);
+ };
+
+ p = createCallbackPair(win, fail);
+ listeners.push(p);
+
+ if (!running) {
+ start();
+ }
+ },
+
+ /**
+ * Asynchronously aquires the acceleration repeatedly at a given interval.
+ *
+ * @param {Function} successCallback The function to call each time the acceleration data is available
+ * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL)
+ * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL)
+ * @return String The watch id that must be passed to #clearWatch to stop watching.
+ */
+ watchAcceleration: function(successCallback, errorCallback, options) {
+ // Default interval (10 sec)
+ var frequency = (options && options.frequency && typeof options.frequency == 'number') ? options.frequency : 10000;
+
+ // successCallback required
+ if (typeof successCallback !== "function") {
+ throw "watchAcceleration must be called with at least a success callback function as first parameter.";
+ }
+
+ // Keep reference to watch id, and report accel readings as often as defined in frequency
+ var id = utils.createUUID();
+
+ var p = createCallbackPair(function(){}, function(e) {
+ removeListeners(p);
+ errorCallback(e);
+ });
+ listeners.push(p);
+
+ timers[id] = {
+ timer:window.setInterval(function() {
+ if (accel) {
+ successCallback(accel);
+ }
+ }, frequency),
+ listeners:p
+ };
+
+ if (running) {
+ // If we're already running then immediately invoke the success callback
+ // but only if we have retreived a value, sample code does not check for null ...
+ if(accel) {
+ successCallback(accel);
+ }
+ } else {
+ start();
+ }
+
+ return id;
+ },
+
+ /**
+ * Clears the specified accelerometer watch.
+ *
+ * @param {String} id The id of the watch returned from #watchAcceleration.
+ */
+ clearWatch: function(id) {
+ // Stop javascript timer & remove from timer list
+ if (id && timers[id]) {
+ window.clearInterval(timers[id].timer);
+ removeListeners(timers[id].listeners);
+ delete timers[id];
+ }
+ }
+};
+
+module.exports = accelerometer;
+
+});
+
+// file: lib/android/plugin/android/app.js
+define("cordova/plugin/android/app", function(require, exports, module) {
+var exec = require('cordova/exec');
+
+module.exports = {
+ /**
+ * Clear the resource cache.
+ */
+ clearCache:function() {
+ exec(null, null, "App", "clearCache", []);
+ },
+
+ /**
+ * Load the url into the webview or into new browser instance.
+ *
+ * @param url The URL to load
+ * @param props Properties that can be passed in to the activity:
+ * wait: int => wait msec before loading URL
+ * loadingDialog: "Title,Message" => display a native loading dialog
+ * loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error
+ * clearHistory: boolean => clear webview history (default=false)
+ * openExternal: boolean => open in a new browser (default=false)
+ *
+ * Example:
+ * navigator.app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000});
+ */
+ loadUrl:function(url, props) {
+ exec(null, null, "App", "loadUrl", [url, props]);
+ },
+
+ /**
+ * Cancel loadUrl that is waiting to be loaded.
+ */
+ cancelLoadUrl:function() {
+ exec(null, null, "App", "cancelLoadUrl", []);
+ },
+
+ /**
+ * Clear web history in this web view.
+ * Instead of BACK button loading the previous web page, it will exit the app.
+ */
+ clearHistory:function() {
+ exec(null, null, "App", "clearHistory", []);
+ },
+
+ /**
+ * Go to previous page displayed.
+ * This is the same as pressing the backbutton on Android device.
+ */
+ backHistory:function() {
+ exec(null, null, "App", "backHistory", []);
+ },
+
+ /**
+ * Override the default behavior of the Android back button.
+ * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired.
+ *
+ * Note: The user should not have to call this method. Instead, when the user
+ * registers for the "backbutton" event, this is automatically done.
+ *
+ * @param override T=override, F=cancel override
+ */
+ overrideBackbutton:function(override) {
+ exec(null, null, "App", "overrideBackbutton", [override]);
+ },
+
+ /**
+ * Exit and terminate the application.
+ */
+ exitApp:function() {
+ return exec(null, null, "App", "exitApp", []);
+ }
+};
+});
+
+// file: lib/android/plugin/android/callback.js
+define("cordova/plugin/android/callback", function(require, exports, module) {
+var port = null,
+ token = null,
+ cordova = require('cordova'),
+ polling = require('cordova/plugin/android/polling'),
+ callback = function() {
+ // Exit if shutting down app
+ if (cordova.shuttingDown) {
+ return;
+ }
+
+ // If polling flag was changed, start using polling from now on
+ if (cordova.UsePolling) {
+ polling();
+ return;
+ }
+
+ var xmlhttp = new XMLHttpRequest();
+
+ // Callback function when XMLHttpRequest is ready
+ xmlhttp.onreadystatechange=function(){
+ if(xmlhttp.readyState === 4){
+
+ // Exit if shutting down app
+ if (cordova.shuttingDown) {
+ return;
+ }
+
+ // If callback has JavaScript statement to execute
+ if (xmlhttp.status === 200) {
+
+ // Need to url decode the response
+ var msg = decodeURIComponent(xmlhttp.responseText);
+ setTimeout(function() {
+ try {
+ var t = eval(msg);
+ }
+ catch (e) {
+ // If we're getting an error here, seeing the message will help in debugging
+ console.log("JSCallback: Message from Server: " + msg);
+ console.log("JSCallback Error: "+e);
+ }
+ }, 1);
+ setTimeout(callback, 1);
+ }
+
+ // If callback ping (used to keep XHR request from timing out)
+ else if (xmlhttp.status === 404) {
+ setTimeout(callback, 10);
+ }
+
+ // If security error
+ else if (xmlhttp.status === 403) {
+ console.log("JSCallback Error: Invalid token. Stopping callbacks.");
+ }
+
+ // If server is stopping
+ else if (xmlhttp.status === 503) {
+ console.log("JSCallback Server Closed: Stopping callbacks.");
+ }
+
+ // If request wasn't GET
+ else if (xmlhttp.status === 400) {
+ console.log("JSCallback Error: Bad request. Stopping callbacks.");
+ }
+
+ // If error, revert to polling
+ else {
+ console.log("JSCallback Error: Request failed.");
+ cordova.UsePolling = true;
+ polling();
+ }
+ }
+ };
+
+ if (port === null) {
+ port = prompt("getPort", "gap_callbackServer:");
+ }
+ if (token === null) {
+ token = prompt("getToken", "gap_callbackServer:");
+ }
+ xmlhttp.open("GET", "http://127.0.0.1:"+port+"/"+token , true);
+ xmlhttp.send();
+};
+
+module.exports = callback;
+});
+
+// file: lib/android/plugin/android/device.js
+define("cordova/plugin/android/device", function(require, exports, module) {
+var channel = require('cordova/channel'),
+ utils = require('cordova/utils'),
+ exec = require('cordova/exec'),
+ app = require('cordova/plugin/android/app');
+
+module.exports = {
+ /*
+ * DEPRECATED
+ * This is only for Android.
+ *
+ * You must explicitly override the back button.
+ */
+ overrideBackButton:function() {
+ console.log("Device.overrideBackButton() is deprecated. Use App.overrideBackbutton(true).");
+ app.overrideBackbutton(true);
+ },
+
+ /*
+ * DEPRECATED
+ * This is only for Android.
+ *
+ * This resets the back button to the default behaviour
+ */
+ resetBackButton:function() {
+ console.log("Device.resetBackButton() is deprecated. Use App.overrideBackbutton(false).");
+ app.overrideBackbutton(false);
+ },
+
+ /*
+ * DEPRECATED
+ * This is only for Android.
+ *
+ * This terminates the activity!
+ */
+ exitApp:function() {
+ console.log("Device.exitApp() is deprecated. Use App.exitApp().");
+ app.exitApp();
+ }
+};
+
+});
+
+// file: lib/android/plugin/android/notification.js
+define("cordova/plugin/android/notification", function(require, exports, module) {
+var exec = require('cordova/exec');
+
+/**
+ * Provides Android enhanced notification API.
+ */
+module.exports = {
+ activityStart : function(title, message) {
+ // If title and message not specified then mimic Android behavior of
+ // using default strings.
+ if (typeof title === "undefined" && typeof message == "undefined") {
+ title = "Busy";
+ message = 'Please wait...';
+ }
+
+ exec(null, null, 'Notification', 'activityStart', [ title, message ]);
+ },
+
+ /**
+ * Close an activity dialog
+ */
+ activityStop : function() {
+ exec(null, null, 'Notification', 'activityStop', []);
+ },
+
+ /**
+ * Display a progress dialog with progress bar that goes from 0 to 100.
+ *
+ * @param {String}
+ * title Title of the progress dialog.
+ * @param {String}
+ * message Message to display in the dialog.
+ */
+ progressStart : function(title, message) {
+ exec(null, null, 'Notification', 'progressStart', [ title, message ]);
+ },
+
+ /**
+ * Close the progress dialog.
+ */
+ progressStop : function() {
+ exec(null, null, 'Notification', 'progressStop', []);
+ },
+
+ /**
+ * Set the progress dialog value.
+ *
+ * @param {Number}
+ * value 0-100
+ */
+ progressValue : function(value) {
+ exec(null, null, 'Notification', 'progressValue', [ value ]);
+ }
+};
+});
+
+// file: lib/android/plugin/android/polling.js
+define("cordova/plugin/android/polling", function(require, exports, module) {
+var cordova = require('cordova'),
+ period = 50,
+ polling = function() {
+ // Exit if shutting down app
+ if (cordova.shuttingDown) {
+ return;
+ }
+
+ // If polling flag was changed, stop using polling from now on and switch to XHR server / callback
+ if (!cordova.UsePolling) {
+ require('cordova/plugin/android/callback')();
+ return;
+ }
+
+ var msg = prompt("", "gap_poll:");
+ if (msg) {
+ setTimeout(function() {
+ try {
+ var t = eval(""+msg);
+ }
+ catch (e) {
+ console.log("JSCallbackPolling: Message from Server: " + msg);
+ console.log("JSCallbackPolling Error: "+e);
+ }
+ }, 1);
+ setTimeout(polling, 1);
+ }
+ else {
+ setTimeout(polling, period);
+ }
+};
+
+module.exports = polling;
+});
+
+// file: lib/android/plugin/android/storage.js
+define("cordova/plugin/android/storage", function(require, exports, module) {
+var utils = require('cordova/utils'),
+ exec = require('cordova/exec'),
+ channel = require('cordova/channel');
+
+var queryQueue = {};
+
+/**
+ * SQL result set object
+ * PRIVATE METHOD
+ * @constructor
+ */
+var DroidDB_Rows = function() {
+ this.resultSet = []; // results array
+ this.length = 0; // number of rows
+};
+
+/**
+ * Get item from SQL result set
+ *
+ * @param row The row number to return
+ * @return The row object
+ */
+DroidDB_Rows.prototype.item = function(row) {
+ return this.resultSet[row];
+};
+
+/**
+ * SQL result set that is returned to user.
+ * PRIVATE METHOD
+ * @constructor
+ */
+var DroidDB_Result = function() {
+ this.rows = new DroidDB_Rows();
+};
+
+/**
+ * Callback from native code when query is complete.
+ * PRIVATE METHOD
+ *
+ * @param id Query id
+ */
+function completeQuery(id, data) {
+ var query = queryQueue[id];
+ if (query) {
+ try {
+ delete queryQueue[id];
+
+ // Get transaction
+ var tx = query.tx;
+
+ // If transaction hasn't failed
+ // Note: We ignore all query results if previous query
+ // in the same transaction failed.
+ if (tx && tx.queryList[id]) {
+
+ // Save query results
+ var r = new DroidDB_Result();
+ r.rows.resultSet = data;
+ r.rows.length = data.length;
+ try {
+ if (typeof query.successCallback === 'function') {
+ query.successCallback(query.tx, r);
+ }
+ } catch (ex) {
+ console.log("executeSql error calling user success callback: "+ex);
+ }
+
+ tx.queryComplete(id);
+ }
+ } catch (e) {
+ console.log("executeSql error: "+e);
+ }
+ }
+}
+
+/**
+ * Callback from native code when query fails
+ * PRIVATE METHOD
+ *
+ * @param reason Error message
+ * @param id Query id
+ */
+function failQuery(reason, id) {
+ var query = queryQueue[id];
+ if (query) {
+ try {
+ delete queryQueue[id];
+
+ // Get transaction
+ var tx = query.tx;
+
+ // If transaction hasn't failed
+ // Note: We ignore all query results if previous query
+ // in the same transaction failed.
+ if (tx && tx.queryList[id]) {
+ tx.queryList = {};
+
+ try {
+ if (typeof query.errorCallback === 'function') {
+ query.errorCallback(query.tx, reason);
+ }
+ } catch (ex) {
+ console.log("executeSql error calling user error callback: "+ex);
+ }
+
+ tx.queryFailed(id, reason);
+ }
+
+ } catch (e) {
+ console.log("executeSql error: "+e);
+ }
+ }
+}
+
+/**
+ * SQL query object
+ * PRIVATE METHOD
+ *
+ * @constructor
+ * @param tx The transaction object that this query belongs to
+ */
+var DroidDB_Query = function(tx) {
+
+ // Set the id of the query
+ this.id = utils.createUUID();
+
+ // Add this query to the queue
+ queryQueue[this.id] = this;
+
+ // Init result
+ this.resultSet = [];
+
+ // Set transaction that this query belongs to
+ this.tx = tx;
+
+ // Add this query to transaction list
+ this.tx.queryList[this.id] = this;
+
+ // Callbacks
+ this.successCallback = null;
+ this.errorCallback = null;
+
+};
+
+/**
+ * Transaction object
+ * PRIVATE METHOD
+ * @constructor
+ */
+var DroidDB_Tx = function() {
+
+ // Set the id of the transaction
+ this.id = utils.createUUID();
+
+ // Callbacks
+ this.successCallback = null;
+ this.errorCallback = null;
+
+ // Query list
+ this.queryList = {};
+};
+
+/**
+ * Mark query in transaction as complete.
+ * If all queries are complete, call the user's transaction success callback.
+ *
+ * @param id Query id
+ */
+DroidDB_Tx.prototype.queryComplete = function(id) {
+ delete this.queryList[id];
+
+ // If no more outstanding queries, then fire transaction success
+ if (this.successCallback) {
+ var count = 0;
+ var i;
+ for (i in this.queryList) {
+ if (this.queryList.hasOwnProperty(i)) {
+ count++;
+ }
+ }
+ if (count === 0) {
+ try {
+ this.successCallback();
+ } catch(e) {
+ console.log("Transaction error calling user success callback: " + e);
+ }
+ }
+ }
+};
+
+/**
+ * Mark query in transaction as failed.
+ *
+ * @param id Query id
+ * @param reason Error message
+ */
+DroidDB_Tx.prototype.queryFailed = function(id, reason) {
+
+ // The sql queries in this transaction have already been run, since
+ // we really don't have a real transaction implemented in native code.
+ // However, the user callbacks for the remaining sql queries in transaction
+ // will not be called.
+ this.queryList = {};
+
+ if (this.errorCallback) {
+ try {
+ this.errorCallback(reason);
+ } catch(e) {
+ console.log("Transaction error calling user error callback: " + e);
+ }
+ }
+};
+
+/**
+ * Execute SQL statement
+ *
+ * @param sql SQL statement to execute
+ * @param params Statement parameters
+ * @param successCallback Success callback
+ * @param errorCallback Error callback
+ */
+DroidDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCallback) {
+
+ // Init params array
+ if (typeof params === 'undefined') {
+ params = [];
+ }
+
+ // Create query and add to queue
+ var query = new DroidDB_Query(this);
+ queryQueue[query.id] = query;
+
+ // Save callbacks
+ query.successCallback = successCallback;
+ query.errorCallback = errorCallback;
+
+ // Call native code
+ exec(null, null, "Storage", "executeSql", [sql, params, query.id]);
+};
+
+var DatabaseShell = function() {
+};
+
+/**
+ * Start a transaction.
+ * Does not support rollback in event of failure.
+ *
+ * @param process {Function} The transaction function
+ * @param successCallback {Function}
+ * @param errorCallback {Function}
+ */
+DatabaseShell.prototype.transaction = function(process, errorCallback, successCallback) {
+ var tx = new DroidDB_Tx();
+ tx.successCallback = successCallback;
+ tx.errorCallback = errorCallback;
+ try {
+ process(tx);
+ } catch (e) {
+ console.log("Transaction error: "+e);
+ if (tx.errorCallback) {
+ try {
+ tx.errorCallback(e);
+ } catch (ex) {
+ console.log("Transaction error calling user error callback: "+e);
+ }
+ }
+ }
+};
+
+/**
+ * Open database
+ *
+ * @param name Database name
+ * @param version Database version
+ * @param display_name Database display name
+ * @param size Database size in bytes
+ * @return Database object
+ */
+var DroidDB_openDatabase = function(name, version, display_name, size) {
+ exec(null, null, "Storage", "openDatabase", [name, version, display_name, size]);
+ var db = new DatabaseShell();
+ return db;
+};
+
+/**
+ * For browsers with no localStorage we emulate it with SQLite. Follows the w3c api.
+ * TODO: Do similar for sessionStorage.
+ * @constructor
+ */
+var CupcakeLocalStorage = function() {
+ channel.waitForInitialization("cupcakeStorage");
+
+ try {
+
+ this.db = openDatabase('localStorage', '1.0', 'localStorage', 2621440);
+ var storage = {};
+ this.length = 0;
+ function setLength (length) {
+ this.length = length;
+ localStorage.length = length;
+ }
+ this.db.transaction(
+ function (transaction) {
+ var i;
+ transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
+ transaction.executeSql('SELECT * FROM storage', [], function(tx, result) {
+ for(var i = 0; i < result.rows.length; i++) {
+ storage[result.rows.item(i).id] = result.rows.item(i).body;
+ }
+ setLength(result.rows.length);
+ channel.initializationComplete("cupcakeStorage");
+ });
+
+ },
+ function (err) {
+ utils.alert(err.message);
+ }
+ );
+ this.setItem = function(key, val) {
+ if (typeof(storage[key])=='undefined') {
+ this.length++;
+ }
+ storage[key] = val;
+ this.db.transaction(
+ function (transaction) {
+ transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
+ transaction.executeSql('REPLACE INTO storage (id, body) values(?,?)', [key,val]);
+ }
+ );
+ };
+ this.getItem = function(key) {
+ return storage[key];
+ };
+ this.removeItem = function(key) {
+ delete storage[key];
+ this.length--;
+ this.db.transaction(
+ function (transaction) {
+ transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
+ transaction.executeSql('DELETE FROM storage where id=?', [key]);
+ }
+ );
+ };
+ this.clear = function() {
+ storage = {};
+ this.length = 0;
+ this.db.transaction(
+ function (transaction) {
+ transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
+ transaction.executeSql('DELETE FROM storage', []);
+ }
+ );
+ };
+ this.key = function(index) {
+ var i = 0;
+ for (var j in storage) {
+ if (i==index) {
+ return j;
+ } else {
+ i++;
+ }
+ }
+ return null;
+ };
+
+ } catch(e) {
+ utils.alert("Database error "+e+".");
+ return;
+ }
+};
+
+module.exports = {
+ openDatabase:DroidDB_openDatabase,
+ CupcakeLocalStorage:CupcakeLocalStorage,
+ failQuery:failQuery,
+ completeQuery:completeQuery
+};
+
+});
+
+// file: lib/common/plugin/battery.js
+define("cordova/plugin/battery", function(require, exports, module) {
+/**
+ * This class contains information about the current battery status.
+ * @constructor
+ */
+var cordova = require('cordova'),
+ exec = require('cordova/exec');
+
+function handlers() {
+ return battery.channels.batterystatus.numHandlers +
+ battery.channels.batterylow.numHandlers +
+ battery.channels.batterycritical.numHandlers;
+}
+
+var Battery = function() {
+ this._level = null;
+ this._isPlugged = null;
+ // Create new event handlers on the window (returns a channel instance)
+ var subscriptionEvents = {
+ onSubscribe:this.onSubscribe,
+ onUnsubscribe:this.onUnsubscribe
+ };
+ this.channels = {
+ batterystatus:cordova.addWindowEventHandler("batterystatus", subscriptionEvents),
+ batterylow:cordova.addWindowEventHandler("batterylow", subscriptionEvents),
+ batterycritical:cordova.addWindowEventHandler("batterycritical", subscriptionEvents)
+ };
+};
+/**
+ * Event handlers for when callbacks get registered for the battery.
+ * Keep track of how many handlers we have so we can start and stop the native battery listener
+ * appropriately (and hopefully save on battery life!).
+ */
+Battery.prototype.onSubscribe = function() {
+ var me = battery;
+ // If we just registered the first handler, make sure native listener is started.
+ if (handlers() === 1) {
+ exec(me._status, me._error, "Battery", "start", []);
+ }
+};
+
+Battery.prototype.onUnsubscribe = function() {
+ var me = battery;
+
+ // If we just unregistered the last handler, make sure native listener is stopped.
+ if (handlers() === 0) {
+ exec(null, null, "Battery", "stop", []);
+ }
+};
+
+/**
+ * Callback for battery status
+ *
+ * @param {Object} info keys: level, isPlugged
+ */
+Battery.prototype._status = function(info) {
+ if (info) {
+ var me = battery;
+ var level = info.level;
+ if (me._level !== level || me._isPlugged !== info.isPlugged) {
+ // Fire batterystatus event
+ cordova.fireWindowEvent("batterystatus", info);
+
+ // Fire low battery event
+ if (level === 20 || level === 5) {
+ if (level === 20) {
+ cordova.fireWindowEvent("batterylow", info);
+ }
+ else {
+ cordova.fireWindowEvent("batterycritical", info);
+ }
+ }
+ }
+ me._level = level;
+ me._isPlugged = info.isPlugged;
+ }
+};
+
+/**
+ * Error callback for battery start
+ */
+Battery.prototype._error = function(e) {
+ console.log("Error initializing Battery: " + e);
+};
+
+var battery = new Battery();
+
+module.exports = battery;
+});
+
+// file: lib/common/plugin/capture.js
+define("cordova/plugin/capture", function(require, exports, module) {
+var exec = require('cordova/exec'),
+ MediaFile = require('cordova/plugin/MediaFile');
+
+/**
+ * Launches a capture of different types.
+ *
+ * @param (DOMString} type
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ * @param {CaptureVideoOptions} options
+ */
+function _capture(type, successCallback, errorCallback, options) {
+ var win = function(pluginResult) {
+ var mediaFiles = [];
+ var i;
+ for (i = 0; i < pluginResult.length; i++) {
+ var mediaFile = new MediaFile();
+ mediaFile.name = pluginResult[i].name;
+ mediaFile.fullPath = pluginResult[i].fullPath;
+ mediaFile.type = pluginResult[i].type;
+ mediaFile.lastModifiedDate = pluginResult[i].lastModifiedDate;
+ mediaFile.size = pluginResult[i].size;
+ mediaFiles.push(mediaFile);
+ }
+ successCallback(mediaFiles);
+ };
+ exec(win, errorCallback, "Capture", type, [options]);
+}
+/**
+ * The Capture interface exposes an interface to the camera and microphone of the hosting device.
+ */
+function Capture() {
+ this.supportedAudioModes = [];
+ this.supportedImageModes = [];
+ this.supportedVideoModes = [];
+}
+
+/**
+ * Launch audio recorder application for recording audio clip(s).
+ *
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ * @param {CaptureAudioOptions} options
+ */
+Capture.prototype.captureAudio = function(successCallback, errorCallback, options){
+ _capture("captureAudio", successCallback, errorCallback, options);
+};
+
+/**
+ * Launch camera application for taking image(s).
+ *
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ * @param {CaptureImageOptions} options
+ */
+Capture.prototype.captureImage = function(successCallback, errorCallback, options){
+ _capture("captureImage", successCallback, errorCallback, options);
+};
+
+/**
+ * Launch device camera application for recording video(s).
+ *
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ * @param {CaptureVideoOptions} options
+ */
+Capture.prototype.captureVideo = function(successCallback, errorCallback, options){
+ _capture("captureVideo", successCallback, errorCallback, options);
+};
+
+
+module.exports = new Capture();
+
+});
+
+// file: lib/common/plugin/compass.js
+define("cordova/plugin/compass", function(require, exports, module) {
+var exec = require('cordova/exec'),
+ utils = require('cordova/utils'),
+ CompassHeading = require('cordova/plugin/CompassHeading'),
+ CompassError = require('cordova/plugin/CompassError'),
+ timers = {},
+ compass = {
+ /**
+ * Asynchronously acquires the current heading.
+ * @param {Function} successCallback The function to call when the heading
+ * data is available
+ * @param {Function} errorCallback The function to call when there is an error
+ * getting the heading data.
+ * @param {CompassOptions} options The options for getting the heading data (not used).
+ */
+ getCurrentHeading:function(successCallback, errorCallback, options) {
+ // successCallback required
+ if (typeof successCallback !== "function") {
+ console.log("Compass Error: successCallback is not a function");
+ return;
+ }
+
+ // errorCallback optional
+ if (errorCallback && (typeof errorCallback !== "function")) {
+ console.log("Compass Error: errorCallback is not a function");
+ return;
+ }
+
+ var win = function(result) {
+ var ch = new CompassHeading(result.magneticHeading, result.trueHeading, result.headingAccuracy, result.timestamp);
+ successCallback(ch);
+ };
+ var fail = function(code) {
+ var ce = new CompassError(code);
+ errorCallback(ce);
+ };
+
+ // Get heading
+ exec(win, fail, "Compass", "getHeading", [options]);
+ },
+
+ /**
+ * Asynchronously acquires the heading repeatedly at a given interval.
+ * @param {Function} successCallback The function to call each time the heading
+ * data is available
+ * @param {Function} errorCallback The function to call when there is an error
+ * getting the heading data.
+ * @param {HeadingOptions} options The options for getting the heading data
+ * such as timeout and the frequency of the watch. For iOS, filter parameter
+ * specifies to watch via a distance filter rather than time.
+ */
+ watchHeading:function(successCallback, errorCallback, options) {
+ // Default interval (100 msec)
+ var frequency = (options !== undefined && options.frequency !== undefined) ? options.frequency : 100;
+ var filter = (options !== undefined && options.filter !== undefined) ? options.filter : 0;
+
+ // successCallback required
+ if (typeof successCallback !== "function") {
+ console.log("Compass Error: successCallback is not a function");
+ return;
+ }
+
+ // errorCallback optional
+ if (errorCallback && (typeof errorCallback !== "function")) {
+ console.log("Compass Error: errorCallback is not a function");
+ return;
+ }
+
+ var id = utils.createUUID();
+ if (filter > 0) {
+ // is an iOS request for watch by filter, no timer needed
+ timers[id] = "iOS";
+ compass.getCurrentHeading(successCallback, errorCallback, options);
+ } else {
+ // Start watch timer to get headings
+ timers[id] = window.setInterval(function() {
+ compass.getCurrentHeading(successCallback, errorCallback);
+ }, frequency);
+ }
+
+ return id;
+ },
+
+ /**
+ * Clears the specified heading watch.
+ * @param {String} watchId The ID of the watch returned from #watchHeading.
+ */
+ clearWatch:function(id) {
+ // Stop javascript timer & remove from timer list
+ if (id && timers[id]) {
+ if (timers[id] != "iOS") {
+ clearInterval(timers[id]);
+ } else {
+ // is iOS watch by filter so call into device to stop
+ exec(null, null, "Compass", "stopHeading", []);
+ }
+ delete timers[id];
+ }
+ }
+ };
+
+module.exports = compass;
+});
+
+// file: lib/common/plugin/console-via-logger.js
+define("cordova/plugin/console-via-logger", function(require, exports, module) {
+//------------------------------------------------------------------------------
+
+var logger = require("cordova/plugin/logger");
+var utils = require("cordova/utils");
+
+//------------------------------------------------------------------------------
+// object that we're exporting
+//------------------------------------------------------------------------------
+var console = module.exports;
+
+//------------------------------------------------------------------------------
+// copy of the original console object
+//------------------------------------------------------------------------------
+var WinConsole = window.console;
+
+//------------------------------------------------------------------------------
+// whether to use the logger
+//------------------------------------------------------------------------------
+var UseLogger = false;
+
+//------------------------------------------------------------------------------
+// Timers
+//------------------------------------------------------------------------------
+var Timers = {};
+
+//------------------------------------------------------------------------------
+// used for unimplemented methods
+//------------------------------------------------------------------------------
+function noop() {}
+
+//------------------------------------------------------------------------------
+// used for unimplemented methods
+//------------------------------------------------------------------------------
+console.useLogger = function (value) {
+ if (arguments.length) UseLogger = !!value;
+
+ if (UseLogger) {
+ if (logger.useConsole()) {
+ throw new Error("console and logger are too intertwingly");
+ }
+ }
+
+ return UseLogger;
+};
+
+//------------------------------------------------------------------------------
+console.log = function() {
+ if (logger.useConsole()) return;
+ logger.log.apply(logger, [].slice.call(arguments));
+};
+
+//------------------------------------------------------------------------------
+console.error = function() {
+ if (logger.useConsole()) return;
+ logger.error.apply(logger, [].slice.call(arguments));
+};
+
+//------------------------------------------------------------------------------
+console.warn = function() {
+ if (logger.useConsole()) return;
+ logger.warn.apply(logger, [].slice.call(arguments));
+};
+
+//------------------------------------------------------------------------------
+console.info = function() {
+ if (logger.useConsole()) return;
+ logger.info.apply(logger, [].slice.call(arguments));
+};
+
+//------------------------------------------------------------------------------
+console.debug = function() {
+ if (logger.useConsole()) return;
+ logger.debug.apply(logger, [].slice.call(arguments));
+};
+
+//------------------------------------------------------------------------------
+console.assert = function(expression) {
+ if (expression) return;
+
+ var message = utils.vformat(arguments[1], [].slice.call(arguments, 2));
+ console.log("ASSERT: " + message);
+};
+
+//------------------------------------------------------------------------------
+console.clear = function() {};
+
+//------------------------------------------------------------------------------
+console.dir = function(object) {
+ console.log("%o", object);
+};
+
+//------------------------------------------------------------------------------
+console.dirxml = function(node) {
+ console.log(node.innerHTML);
+};
+
+//------------------------------------------------------------------------------
+console.trace = noop;
+
+//------------------------------------------------------------------------------
+console.group = console.log;
+
+//------------------------------------------------------------------------------
+console.groupCollapsed = console.log;
+
+//------------------------------------------------------------------------------
+console.groupEnd = noop;
+
+//------------------------------------------------------------------------------
+console.time = function(name) {
+ Timers[name] = new Date().valueOf();
+};
+
+//------------------------------------------------------------------------------
+console.timeEnd = function(name) {
+ var timeStart = Timers[name];
+ if (!timeStart) {
+ console.warn("unknown timer: " + name);
+ return;
+ }
+
+ var timeElapsed = new Date().valueOf() - timeStart;
+ console.log(name + ": " + timeElapsed + "ms");
+};
+
+//------------------------------------------------------------------------------
+console.timeStamp = noop;
+
+//------------------------------------------------------------------------------
+console.profile = noop;
+
+//------------------------------------------------------------------------------
+console.profileEnd = noop;
+
+//------------------------------------------------------------------------------
+console.count = noop;
+
+//------------------------------------------------------------------------------
+console.exception = console.log;
+
+//------------------------------------------------------------------------------
+console.table = function(data, columns) {
+ console.log("%o", data);
+};
+
+//------------------------------------------------------------------------------
+// return a new function that calls both functions passed as args
+//------------------------------------------------------------------------------
+function wrapperedOrigCall(orgFunc, newFunc) {
+ return function() {
+ var args = [].slice.call(arguments);
+ try { orgFunc.apply(WinConsole, args); } catch (e) {}
+ try { newFunc.apply(console, args); } catch (e) {}
+ };
+}
+
+//------------------------------------------------------------------------------
+// For every function that exists in the original console object, that
+// also exists in the new console object, wrap the new console method
+// with one that calls both
+//------------------------------------------------------------------------------
+for (var key in console) {
+ if (typeof WinConsole[key] == "function") {
+ console[key] = wrapperedOrigCall(WinConsole[key], console[key]);
+ }
+}
+
+});
+
+// file: lib/common/plugin/contacts.js
+define("cordova/plugin/contacts", function(require, exports, module) {
+var exec = require('cordova/exec'),
+ ContactError = require('cordova/plugin/ContactError'),
+ utils = require('cordova/utils'),
+ Contact = require('cordova/plugin/Contact');
+
+/**
+* Represents a group of Contacts.
+* @constructor
+*/
+var contacts = {
+ /**
+ * Returns an array of Contacts matching the search criteria.
+ * @param fields that should be searched
+ * @param successCB success callback
+ * @param errorCB error callback
+ * @param {ContactFindOptions} options that can be applied to contact searching
+ * @return array of Contacts matching search criteria
+ */
+ find:function(fields, successCB, errorCB, options) {
+ if (!successCB) {
+ throw new TypeError("You must specify a success callback for the find command.");
+ }
+ if (!fields || (utils.isArray(fields) && fields.length === 0)) {
+ if (typeof errorCB === "function") {
+ errorCB(new ContactError(ContactError.INVALID_ARGUMENT_ERROR));
+ }
+ } else {
+ var win = function(result) {
+ var cs = [];
+ for (var i = 0, l = result.length; i < l; i++) {
+ cs.push(contacts.create(result[i]));
+ }
+ successCB(cs);
+ };
+ exec(win, errorCB, "Contacts", "search", [fields, options]);
+ }
+ },
+
+ /**
+ * This function creates a new contact, but it does not persist the contact
+ * to device storage. To persist the contact to device storage, invoke
+ * contact.save().
+ * @param properties an object who's properties will be examined to create a new Contact
+ * @returns new Contact object
+ */
+ create:function(properties) {
+ var i;
+ var contact = new Contact();
+ for (i in properties) {
+ if (typeof contact[i] !== 'undefined' && properties.hasOwnProperty(i)) {
+ contact[i] = properties[i];
+ }
+ }
+ return contact;
+ }
+};
+
+module.exports = contacts;
+
+});
+
+// file: lib/common/plugin/device.js
+define("cordova/plugin/device", function(require, exports, module) {
+var channel = require('cordova/channel'),
+ utils = require('cordova/utils'),
+ exec = require('cordova/exec');
+
+// Tell cordova channel to wait on the CordovaInfoReady event
+channel.waitForInitialization('onCordovaInfoReady');
+
+/**
+ * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the
+ * phone, etc.
+ * @constructor
+ */
+function Device() {
+ this.available = false;
+ this.platform = null;
+ this.version = null;
+ this.name = null;
+ this.uuid = null;
+ this.cordova = null;
+
+ var me = this;
+
+ channel.onCordovaReady.subscribeOnce(function() {
+ me.getInfo(function(info) {
+ me.available = true;
+ me.platform = info.platform;
+ me.version = info.version;
+ me.name = info.name;
+ me.uuid = info.uuid;
+ me.cordova = info.cordova;
+ channel.onCordovaInfoReady.fire();
+ },function(e) {
+ me.available = false;
+ utils.alert("[ERROR] Error initializing Cordova: " + e);
+ });
+ });
+}
+
+/**
+ * Get device info
+ *
+ * @param {Function} successCallback The function to call when the heading data is available
+ * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL)
+ */
+Device.prototype.getInfo = function(successCallback, errorCallback) {
+
+ // successCallback required
+ if (typeof successCallback !== "function") {
+ console.log("Device Error: successCallback is not a function");
+ return;
+ }
+
+ // errorCallback optional
+ if (errorCallback && (typeof errorCallback !== "function")) {
+ console.log("Device Error: errorCallback is not a function");
+ return;
+ }
+
+ // Get info
+ exec(successCallback, errorCallback, "Device", "getDeviceInfo", []);
+};
+
+module.exports = new Device();
+
+});
+
+// file: lib/common/plugin/geolocation.js
+define("cordova/plugin/geolocation", function(require, exports, module) {
+var utils = require('cordova/utils'),
+ exec = require('cordova/exec'),
+ PositionError = require('cordova/plugin/PositionError'),
+ Position = require('cordova/plugin/Position');
+
+var timers = {}; // list of timers in use
+
+// Returns default params, overrides if provided with values
+function parseParameters(options) {
+ var opt = {
+ maximumAge: 0,
+ enableHighAccuracy: false,
+ timeout: Infinity
+ };
+
+ if (options) {
+ if (options.maximumAge !== undefined && !isNaN(options.maximumAge) && options.maximumAge > 0) {
+ opt.maximumAge = options.maximumAge;
+ }
+ if (options.enableHighAccuracy !== undefined) {
+ opt.enableHighAccuracy = options.enableHighAccuracy;
+ }
+ if (options.timeout !== undefined && !isNaN(options.timeout)) {
+ if (options.timeout < 0) {
+ opt.timeout = 0;
+ } else {
+ opt.timeout = options.timeout;
+ }
+ }
+ }
+
+ return opt;
+}
+
+// Returns a timeout failure, closed over a specified timeout value and error callback.
+function createTimeout(errorCallback, timeout) {
+ var t = setTimeout(function() {
+ clearTimeout(t);
+ t = null;
+ errorCallback({
+ code:PositionError.TIMEOUT,
+ message:"Position retrieval timed out."
+ });
+ }, timeout);
+ return t;
+}
+
+var geolocation = {
+ lastPosition:null, // reference to last known (cached) position returned
+ /**
+ * Asynchronously aquires the current position.
+ *
+ * @param {Function} successCallback The function to call when the position data is available
+ * @param {Function} errorCallback The function to call when there is an error getting the heading position. (OPTIONAL)
+ * @param {PositionOptions} options The options for getting the position data. (OPTIONAL)
+ */
+ getCurrentPosition:function(successCallback, errorCallback, options) {
+ if (arguments.length === 0) {
+ throw new Error("getCurrentPosition must be called with at least one argument.");
+ }
+ options = parseParameters(options);
+
+ // Timer var that will fire an error callback if no position is retrieved from native
+ // before the "timeout" param provided expires
+ var timeoutTimer = null;
+
+ var win = function(p) {
+ clearTimeout(timeoutTimer);
+ if (!timeoutTimer) {
+ // Timeout already happened, or native fired error callback for
+ // this geo request.
+ // Don't continue with success callback.
+ return;
+ }
+ var pos = new Position(
+ {
+ latitude:p.latitude,
+ longitude:p.longitude,
+ altitude:p.altitude,
+ accuracy:p.accuracy,
+ heading:p.heading,
+ velocity:p.velocity,
+ altitudeAccuracy:p.altitudeAccuracy
+ },
+ (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp)))
+ );
+ geolocation.lastPosition = pos;
+ successCallback(pos);
+ };
+ var fail = function(e) {
+ clearTimeout(timeoutTimer);
+ timeoutTimer = null;
+ var err = new PositionError(e.code, e.message);
+ if (errorCallback) {
+ errorCallback(err);
+ }
+ };
+
+ // Check our cached position, if its timestamp difference with current time is less than the maximumAge, then just
+ // fire the success callback with the cached position.
+ if (geolocation.lastPosition && options.maximumAge && (((new Date()).getTime() - geolocation.lastPosition.timestamp.getTime()) <= options.maximumAge)) {
+ successCallback(geolocation.lastPosition);
+ // If the cached position check failed and the timeout was set to 0, error out with a TIMEOUT error object.
+ } else if (options.timeout === 0) {
+ fail({
+ code:PositionError.TIMEOUT,
+ message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceed's provided PositionOptions' maximumAge parameter."
+ });
+ // Otherwise we have to call into native to retrieve a position.
+ } else {
+ if (options.timeout !== Infinity) {
+ // If the timeout value was not set to Infinity (default), then
+ // set up a timeout function that will fire the error callback
+ // if no successful position was retrieved before timeout expired.
+ timeoutTimer = createTimeout(fail, options.timeout);
+ } else {
+ // This is here so the check in the win function doesn't mess stuff up
+ // may seem weird but this guarantees timeoutTimer is
+ // always truthy before we call into native
+ timeoutTimer = true;
+ }
+ exec(win, fail, "Geolocation", "getLocation", [options.enableHighAccuracy, options.maximumAge]);
+ }
+ return timeoutTimer;
+ },
+ /**
+ * Asynchronously watches the geolocation for changes to geolocation. When a change occurs,
+ * the successCallback is called with the new location.
+ *
+ * @param {Function} successCallback The function to call each time the location data is available
+ * @param {Function} errorCallback The function to call when there is an error getting the location data. (OPTIONAL)
+ * @param {PositionOptions} options The options for getting the location data such as frequency. (OPTIONAL)
+ * @return String The watch id that must be passed to #clearWatch to stop watching.
+ */
+ watchPosition:function(successCallback, errorCallback, options) {
+ if (arguments.length === 0) {
+ throw new Error("watchPosition must be called with at least one argument.");
+ }
+ options = parseParameters(options);
+
+ var id = utils.createUUID();
+
+ // Tell device to get a position ASAP, and also retrieve a reference to the timeout timer generated in getCurrentPosition
+ timers[id] = geolocation.getCurrentPosition(successCallback, errorCallback, options);
+
+ var fail = function(e) {
+ clearTimeout(timers[id]);
+ var err = new PositionError(e.code, e.message);
+ if (errorCallback) {
+ errorCallback(err);
+ }
+ };
+
+ var win = function(p) {
+ clearTimeout(timers[id]);
+ if (options.timeout !== Infinity) {
+ timers[id] = createTimeout(fail, options.timeout);
+ }
+ var pos = new Position(
+ {
+ latitude:p.latitude,
+ longitude:p.longitude,
+ altitude:p.altitude,
+ accuracy:p.accuracy,
+ heading:p.heading,
+ velocity:p.velocity,
+ altitudeAccuracy:p.altitudeAccuracy
+ },
+ (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp)))
+ );
+ geolocation.lastPosition = pos;
+ successCallback(pos);
+ };
+
+ exec(win, fail, "Geolocation", "addWatch", [id, options.enableHighAccuracy]);
+
+ return id;
+ },
+ /**
+ * Clears the specified heading watch.
+ *
+ * @param {String} id The ID of the watch returned from #watchPosition
+ */
+ clearWatch:function(id) {
+ if (id && timers[id] !== undefined) {
+ clearTimeout(timers[id]);
+ delete timers[id];
+ exec(null, null, "Geolocation", "clearWatch", [id]);
+ }
+ }
+};
+
+module.exports = geolocation;
+
+});
+
+// file: lib/common/plugin/logger.js
+define("cordova/plugin/logger", function(require, exports, module) {
+//------------------------------------------------------------------------------
+// The logger module exports the following properties/functions:
+//
+// LOG - constant for the level LOG
+// ERROR - constant for the level ERROR
+// WARN - constant for the level WARN
+// INFO - constant for the level INFO
+// DEBUG - constant for the level DEBUG
+// logLevel() - returns current log level
+// logLevel(value) - sets and returns a new log level
+// useConsole() - returns whether logger is using console
+// useConsole(value) - sets and returns whether logger is using console
+// log(message,...) - logs a message at level LOG
+// error(message,...) - logs a message at level ERROR
+// warn(message,...) - logs a message at level WARN
+// info(message,...) - logs a message at level INFO
+// debug(message,...) - logs a message at level DEBUG
+// logLevel(level,message,...) - logs a message specified level
+//
+//------------------------------------------------------------------------------
+
+var logger = exports;
+
+var exec = require('cordova/exec');
+var utils = require('cordova/utils');
+
+var UseConsole = true;
+var Queued = [];
+var DeviceReady = false;
+var CurrentLevel;
+
+/**
+ * Logging levels
+ */
+
+var Levels = [
+ "LOG",
+ "ERROR",
+ "WARN",
+ "INFO",
+ "DEBUG"
+];
+
+/*
+ * add the logging levels to the logger object and
+ * to a separate levelsMap object for testing
+ */
+
+var LevelsMap = {};
+for (var i=0; i CurrentLevel) return;
+
+ // queue the message if not yet at deviceready
+ if (!DeviceReady && !UseConsole) {
+ Queued.push([level, message]);
+ return;
+ }
+
+ // if not using the console, use the native logger
+ if (!UseConsole) {
+ exec(null, null, "Logger", "logLevel", [level, message]);
+ return;
+ }
+
+ // make sure console is not using logger
+ if (console.__usingCordovaLogger) {
+ throw new Error("console and logger are too intertwingly");
+ }
+
+ // log to the console
+ switch (level) {
+ case logger.LOG: console.log(message); break;
+ case logger.ERROR: console.log("ERROR: " + message); break;
+ case logger.WARN: console.log("WARN: " + message); break;
+ case logger.INFO: console.log("INFO: " + message); break;
+ case logger.DEBUG: console.log("DEBUG: " + message); break;
+ }
+};
+
+// when deviceready fires, log queued messages
+logger.__onDeviceReady = function() {
+ if (DeviceReady) return;
+
+ DeviceReady = true;
+
+ for (var i=0; i 3) {
+ fail(FileError.SYNTAX_ERR);
+ } else {
+ // if successful, return a FileSystem object
+ var success = function(file_system) {
+ if (file_system) {
+ if (typeof successCallback === 'function') {
+ // grab the name and root from the file system object
+ var result = new FileSystem(file_system.name, file_system.root);
+ successCallback(result);
+ }
+ }
+ else {
+ // no FileSystem object returned
+ fail(FileError.NOT_FOUND_ERR);
+ }
+ };
+ exec(success, fail, "File", "requestFileSystem", [type, size]);
+ }
+};
+
+module.exports = requestFileSystem;
+});
+
+// file: lib/common/plugin/resolveLocalFileSystemURI.js
+define("cordova/plugin/resolveLocalFileSystemURI", function(require, exports, module) {
+var DirectoryEntry = require('cordova/plugin/DirectoryEntry'),
+ FileEntry = require('cordova/plugin/FileEntry'),
+ FileError = require('cordova/plugin/FileError'),
+ exec = require('cordova/exec');
+
+/**
+ * Look up file system Entry referred to by local URI.
+ * @param {DOMString} uri URI referring to a local file or directory
+ * @param successCallback invoked with Entry object corresponding to URI
+ * @param errorCallback invoked if error occurs retrieving file system entry
+ */
+module.exports = function(uri, successCallback, errorCallback) {
+ // error callback
+ var fail = function(error) {
+ if (typeof errorCallback === 'function') {
+ errorCallback(new FileError(error));
+ }
+ };
+ // sanity check for 'not:valid:filename'
+ if(!uri || uri.split(":").length > 2) {
+ setTimeout( function() {
+ fail(FileError.ENCODING_ERR);
+ },0);
+ return;
+ }
+ // if successful, return either a file or directory entry
+ var success = function(entry) {
+ var result;
+ if (entry) {
+ if (typeof successCallback === 'function') {
+ // create appropriate Entry object
+ result = (entry.isDirectory) ? new DirectoryEntry(entry.name, entry.fullPath) : new FileEntry(entry.name, entry.fullPath);
+ try {
+ successCallback(result);
+ }
+ catch (e) {
+ console.log('Error invoking callback: ' + e);
+ }
+ }
+ }
+ else {
+ // no Entry object returned
+ fail(FileError.NOT_FOUND_ERR);
+ }
+ };
+
+ exec(success, fail, "File", "resolveLocalFileSystemURI", [uri]);
+};
+
+});
+
+// file: lib/common/plugin/splashscreen.js
+define("cordova/plugin/splashscreen", function(require, exports, module) {
+var exec = require('cordova/exec');
+
+var splashscreen = {
+ show:function() {
+ exec(null, null, "SplashScreen", "show", []);
+ },
+ hide:function() {
+ exec(null, null, "SplashScreen", "hide", []);
+ }
+};
+
+module.exports = splashscreen;
+});
+
+// file: lib/common/utils.js
+define("cordova/utils", function(require, exports, module) {
+var utils = exports;
+
+/**
+ * Returns an indication of whether the argument is an array or not
+ */
+utils.isArray = function(a) {
+ return Object.prototype.toString.call(a) == '[object Array]';
+};
+
+/**
+ * Returns an indication of whether the argument is a Date or not
+ */
+utils.isDate = function(d) {
+ return Object.prototype.toString.call(d) == '[object Date]';
+};
+
+/**
+ * Does a deep clone of the object.
+ */
+utils.clone = function(obj) {
+ if(!obj || typeof obj == 'function' || utils.isDate(obj) || typeof obj != 'object') {
+ return obj;
+ }
+
+ var retVal, i;
+
+ if(utils.isArray(obj)){
+ retVal = [];
+ for(i = 0; i < obj.length; ++i){
+ retVal.push(utils.clone(obj[i]));
+ }
+ return retVal;
+ }
+
+ retVal = {};
+ for(i in obj){
+ if(!(i in retVal) || retVal[i] != obj[i]) {
+ retVal[i] = utils.clone(obj[i]);
+ }
+ }
+ return retVal;
+};
+
+/**
+ * Returns a wrappered version of the function
+ */
+utils.close = function(context, func, params) {
+ if (typeof params == 'undefined') {
+ return function() {
+ return func.apply(context, arguments);
+ };
+ } else {
+ return function() {
+ return func.apply(context, params);
+ };
+ }
+};
+
+/**
+ * Create a UUID
+ */
+utils.createUUID = function() {
+ return UUIDcreatePart(4) + '-' +
+ UUIDcreatePart(2) + '-' +
+ UUIDcreatePart(2) + '-' +
+ UUIDcreatePart(2) + '-' +
+ UUIDcreatePart(6);
+};
+
+/**
+ * Extends a child object from a parent object using classical inheritance
+ * pattern.
+ */
+utils.extend = (function() {
+ // proxy used to establish prototype chain
+ var F = function() {};
+ // extend Child from Parent
+ return function(Child, Parent) {
+ F.prototype = Parent.prototype;
+ Child.prototype = new F();
+ Child.__super__ = Parent.prototype;
+ Child.prototype.constructor = Child;
+ };
+}());
+
+/**
+ * Alerts a message in any available way: alert or console.log.
+ */
+utils.alert = function(msg) {
+ if (alert) {
+ alert(msg);
+ } else if (console && console.log) {
+ console.log(msg);
+ }
+};
+
+/**
+ * Formats a string and arguments following it ala sprintf()
+ *
+ * see utils.vformat() for more information
+ */
+utils.format = function(formatString /* ,... */) {
+ var args = [].slice.call(arguments, 1);
+ return utils.vformat(formatString, args);
+};
+
+/**
+ * Formats a string and arguments following it ala vsprintf()
+ *
+ * format chars:
+ * %j - format arg as JSON
+ * %o - format arg as JSON
+ * %c - format arg as ''
+ * %% - replace with '%'
+ * any other char following % will format it's
+ * arg via toString().
+ *
+ * for rationale, see FireBug's Console API:
+ * http://getfirebug.com/wiki/index.php/Console_API
+ */
+utils.vformat = function(formatString, args) {
+ if (formatString === null || formatString === undefined) return "";
+ if (arguments.length == 1) return formatString.toString();
+ if (typeof formatString != "string") return formatString.toString();
+
+ var pattern = /(.*?)%(.)(.*)/;
+ var rest = formatString;
+ var result = [];
+
+ while (args.length) {
+ var arg = args.shift();
+ var match = pattern.exec(rest);
+
+ if (!match) break;
+
+ rest = match[3];
+
+ result.push(match[1]);
+
+ if (match[2] == '%') {
+ result.push('%');
+ args.unshift(arg);
+ continue;
+ }
+
+ result.push(formatted(arg, match[2]));
+ }
+
+ result.push(rest);
+
+ return result.join('');
+};
+
+//------------------------------------------------------------------------------
+function UUIDcreatePart(length) {
+ var uuidpart = "";
+ for (var i=0; i
+
+
+
+
+
+
+ PhoneGap and Wikitude
+
+
+
+
+
+
+
+ Hello Cordova
+
+
+
+
Apache Cordovaâ„¢
+
+
Connecting to Device
+
Device is Ready
+
+
+
+
+
+
+
+
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/js/WikitudePlugin.js b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/js/WikitudePlugin.js
new file mode 100644
index 0000000..bd09eb2
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/js/WikitudePlugin.js
@@ -0,0 +1,383 @@
+var WikitudePlugin = {
+
+ /**
+ *
+ * This is the SDK Key, provided to you after you purchased the Wikitude SDK from http://www.wikitude.com/developer/sdk
+ * If you're having a trial version, leave this string empty
+ *
+ */
+ mySDKKey : "ENTER-YOUR-KEY-HERE",
+
+ /** true if architectview is open */
+ isOpened : false,
+
+ /**
+ *
+ * called when user pressed back-button on Android device
+ *
+ */
+ backButtonCallback : null,
+
+ /**
+ *
+ * Change the value of this variable to modify the location update rate
+ *
+ */
+ locationUpdateRate : 3000,
+
+ /**
+ *
+ * This variable represents if the current device is capable of running the Wikitude SDK
+ *
+ */
+ isDeviceSupported : false,
+
+ /**
+ *
+ * This watchID is used to shedule location updates
+ *
+ */
+ watchID : null,
+
+ /**
+ *
+ * Callbacks to get device information if ARchitect Worlds can be launched
+ *
+ */
+ onDeviceSupportedCallback : null,
+ onDeviceNotSupportedCallback : null,
+
+ /**
+ *
+ * Callbacks to get notified if the ARchitect World finished launching or if something went wrong during the World launch
+ *
+ */
+ onARchitectWorldLaunchedCallback : null,
+ onARchitectWorldFailedLaunchingCallback : null,
+
+ /**
+ *
+ * This function gets called if PhoneGap reports that it has finished loading successfully.
+ *
+ */
+ isDeviceSupported: function(successCallback, errorCallback)
+ {
+
+ WikitudePlugin.onDeviceSupportedCallback = successCallback;
+ WikitudePlugin.onDeviceNotSupportedCallback = errorCallback;
+
+
+ // PhoneGap is running, so the first thing we do is to check if the current device is capable of running the Wikitude Plugin
+ cordova.exec(WikitudePlugin.deviceIsARchitectReady, WikitudePlugin.deviceIsNotARchitectReady, "WikitudePlugin", "isDeviceSupported", [""]);
+
+ },
+
+ /**
+ *
+ * Declare what should happen when user pressed back-button while architect-view is opened.
+ * If not declared/called or null: ARchitect-View is closed
+ *
+ */
+ onPressedBackButton : function(callback)
+ {
+ WikitudePlugin.backButtonCallback = callback;
+ },
+
+ /**
+ *
+ * This function gets called if the Wikitude Plugin reports that the device is able to start the Wikitude SDK
+ *
+ */
+ deviceIsARchitectReady : function()
+ {
+ // We keep track of the device status
+ WikitudePlugin.isDeviceSupported = true;
+
+
+ if(WikitudePlugin.onDeviceSupportedCallback)
+ {
+ WikitudePlugin.onDeviceSupportedCallback();
+ }
+ },
+
+ /**
+ *
+ * This function gets called if the Wikitude Plugin reports that the device is not able of starting the Wikitude SDK.
+ *
+ */
+ deviceIsNotARchitectReady : function()
+ {
+ WikitudePlugin.isDeviceSupported = false;
+
+ // In this case we notify the user that his device is not supported by the Wikitude SDK
+ if(WikitudePlugin.onDeviceNotSupportedCallback)
+ {
+ WikitudePlugin.onDeviceNotSupportedCallback();
+ }
+ },
+
+ /*
+ * =============================================================================================================================
+ *
+ * PUBLIC API
+ *
+ * =============================================================================================================================
+ */
+
+ /* Managing ARchitect world loading */
+
+ /**
+ *
+ * Call this function if you want to load an ARchitect World
+ *
+ * @param {String} worldPath The path to an ARchitect world ether on the device or on e.g. your dropbox
+ *
+ */
+ loadARchitectWorld : function(worldPath)
+ {
+
+ // before we actually call load, we check again if the device is able to open the world
+ if(WikitudePlugin.isDeviceSupported)
+ {
+
+ // the 'open' function of the Wikitude Plugin requires a option dictionary with two keys:
+ // @param {Object} options (required)
+ // @param {String} options.sdkKey License key for the Wikitude SDK
+ // @param {String} options.filePath The path to a local ARchitect world or to a ARchitect world on a server or your dropbox
+
+ cordova.exec(WikitudePlugin.worldLaunched, WikitudePlugin.worldFailedLaunching, "WikitudePlugin", "open", [{ sdkKey: WikitudePlugin.mySDKKey, filePath: worldPath}]);
+
+
+ // We add an event listener on the resume and pause event of the application lifecycle
+ document.addEventListener("resume", WikitudePlugin.onResume, false);
+ document.addEventListener("pause", WikitudePlugin.onPause, false);
+
+ WikitudePlugin.isOpened = true;
+ document.addEventListener("backbutton", WikitudePlugin.onBackButton, false);
+
+ // After we started loading the world, we start location updates
+ WikitudePlugin.startLocationUpdates();
+
+ }else
+ {
+ // if the device is not able to start the Wikitude SDK, we notify the user again
+ WikitudePlugin.deviceNotARchitectReady();
+ }
+ },
+
+ /* Managing the Wikitude SDK Lifecycle */
+
+ /**
+ *
+ * Use this function to stop the Wikitude SDK and to remove the ARchitectView from the screen
+ *
+ */
+ close : function()
+ {
+ document.removeEventListener("pause", WikitudePlugin.onPause, false);
+ document.removeEventListener("resume", WikitudePlugin.onResume, false);
+
+ WikitudePlugin.stopLocationUpdates();
+
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "close", [""]);
+ WikitudePlugin.isOpened = false;
+ },
+
+ /**
+ *
+ * Use this function to only hide the Wikitude SDK. All location and rendering updates are still active
+ *
+ */
+ hide : function()
+ {
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "hide", [""]);
+ },
+
+ /**
+ *
+ * Use this function to show the Wikitude SDK if it was hidden before
+ *
+ */
+ show : function()
+ {
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "show", [""]);
+ },
+
+ /* Interacting with the Wikitude SDK */
+
+ /**
+ *
+ * Use this function to call javascript which will be executed in the context of your ARchitect World
+ *
+ *
+ * @param js The JavaScript that gets evaluated in context of the ARchitect World
+ *
+ */
+ callJavaScript : function(js)
+ {
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "callJavascript", [js]);
+ },
+
+ /**
+ *
+ * Use this function to set a callback which will be invoked when the ARchitect World calls for example
+ * document.location = "architectsdk://opendetailpage?id=9";
+ *
+ *
+ * @param onUrlInvokeCallback A function which gets called when the ARchitect World invokes a call to "document.location = architectsdk://"
+ */
+ setOnUrlInvokeCallback : function(onUrlInvokeCallback)
+ {
+ cordova.exec(onUrlInvokeCallback, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onUrlInvoke", [""]);
+ },
+
+
+ /*
+ * =============================================================================================================================
+ *
+ * Callbacks of public functions
+ *
+ * =============================================================================================================================
+ */
+
+ /**
+ *
+ * Use this callback to get notified if the world loaded successfully
+ *
+ */
+ worldLaunched : function()
+ {
+ if(WikitudePlugin.onARchitectWorldLaunchedCallback)
+ {
+ WikitudePlugin.onARchitectWorldLaunchedCallback();
+ }
+ },
+
+ /**
+ *
+ * Use this callback to get notified if the Wikitude SDK wasn't able to load the ARchitect World
+ *
+ */
+ worldFailedLaunching : function(err)
+ {
+ if(WikitudePlugin.onARchitectWorldFailedLaunchingCallback)
+ {
+ WikitudePlugin.onARchitectWorldFailedLaunchingCallback(err);
+ }
+ },
+
+ /* Lifecycle updates */
+
+ /**
+ *
+ * This function actually starts the PhoneGap location updates
+ *
+ */
+ startLocationUpdates : function()
+ {
+
+ WikitudePlugin.watchID = navigator.geolocation.watchPosition(WikitudePlugin.onReceivedLocation, WikitudePlugin.onWikitudeError, { frequency: WikitudePlugin.locationUpdateRate });
+ },
+
+ /**
+ *
+ * This callback gets called everytime the location did update
+ *
+ */
+ onReceivedLocation : function(position)
+ {
+
+ // Every time that PhoneGap did received a location update, we pass the location into the Wikitude SDK
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "setLocation", [{ lat: position.coords.latitude, lon: position.coords.longitude, alt: position.coords.altitude, acc: position.coords.accuracy}]);
+ },
+
+ /**
+ *
+ * Use this function to stop location updates
+ *
+ */
+ stopLocationUpdates : function()
+ {
+
+ // We clear the location update watch which was responsible for updating the location in a specific time interval
+ navigator.geolocation.clearWatch(WikitudePlugin.watchID);
+ WikitudePlugin.watchID = null;
+ },
+
+ /**
+ *
+ * This function gets called every time the application did become active.
+ *
+ */
+ onResume : function()
+ {
+
+ // Call the Wikitude SDK that the application did become active again
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onResume", [""]);
+
+ // And start continuing updating the user location
+ WikitudePlugin.startLocationUpdates();
+ },
+
+ /**
+ *
+ * This function gets called every time the application is about to become inactive
+ *
+ */
+ onPause : function()
+ {
+
+ // Call the Wikitude SDK that the application did resign active
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onPause", [""]);
+
+ // And stop all ongoing location updates
+ WikitudePlugin.stopLocationUpdates();
+ },
+
+ /**
+ *
+ * Android specific!
+ * This function gets called if the user presses the back button.
+ * You may define your own implementation by using WikitudePlugin.onPressedBackButton(method)
+ *
+ */
+ onBackButton : function()
+ {
+ if (WikitudePlugin.isOpened) {
+ if (WikitudePlugin.backButtonCallback==null) {
+ WikitudePlugin.close();
+ document.removeEventListener("backbutton", WikitudePlugin.onBackButton, false);
+ } else {
+ WikitudePlugin.backButtonCallback();
+ }
+ }
+ },
+
+ /**
+ *
+ * This function gets called every time the application is about to become inactive
+ *
+ */
+ onWikitudeOK : function()
+ {
+ },
+
+ /**
+ *
+ * This function gets called every time the application is about to become inactive
+ *
+ */
+ onWikitudeError : function()
+ {
+ },
+
+ /**
+ *
+ * This function gets called every time the application is about to become inactive
+ *
+ */
+ report: function(id)
+ {
+ console.log("app report: " + id);
+ }
+};
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/js/index.js b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/js/index.js
new file mode 100644
index 0000000..412a0fe
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/js/index.js
@@ -0,0 +1,116 @@
+var app = {
+ initialize: function() {
+ this.bind();
+ },
+ bind: function() {
+ document.addEventListener('deviceready', this.deviceready, false);
+ },
+
+ /**
+ *
+ * This function extracts an url parameter
+ *
+ */
+ getUrlParameterForKey : function( url, requestedParam )
+ {
+ requestedParam = requestedParam.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
+ var regexS = "[\\?&]"+requestedParam+"=([^]*)";
+ var regex = new RegExp( regexS );
+ var results = regex.exec( url );
+ if( results == null )
+ return "";
+ else
+ {
+ var result = decodeURIComponent(results[1]);
+ return result;
+ }
+
+ },
+
+ /**
+ *
+ * This function gets called if you call "document.location = architectsdk://" in your ARchitect World
+ *
+ *
+ * @param url The url which was called in ARchitect World
+ *
+ */
+ onClickInARchitectWorld : function(url)
+ {
+
+ app.report("you clicked on a label with text: " + app.getUrlParameterForKey(url, 'text'));
+ },
+
+ /**
+ *
+ * This function gets called it the Wikitude SDK is able to start an ARchitect World (the current device is supported by the Wikitude SDK)
+ *
+ */
+ onDeviceIsReadyCallback : function()
+ {
+ // The device is able to launch ARchitect World, so we load the 'Hello World' example
+ WikitudePlugin.loadARchitectWorld("assets/world/HelloWorld.html");
+
+ // To be able to respond on events inside the ARchitect World, we set a onURLInvoke callback
+ WikitudePlugin.setOnUrlInvokeCallback(app.onClickInARchitectWorld);
+
+ // This is a example how you can interact with the ARchitect World to pass in additional information
+ // In this example, a JavaScript function gets called which sets a new text for a label
+ WikitudePlugin.callJavaScript("didReceivedNewTextForLabel('Hello World')");
+ },
+
+ /**
+ *
+ * This function gets if the current device is not capable of running ARchitect Worlds
+ *
+ */
+ onDeviceIsUnsupportedCallback : function()
+ {
+ app.report('device is not supported');
+ },
+
+ /**
+ *
+ * This function gets if the ARchitect World finished loading
+ *
+ */
+ onARchitectWorldLaunchedCallback : function()
+ {
+ app.report('ARchitect World launched');
+ },
+
+ /**
+ *
+ * This function gets if the ARchitect failed loading
+ *
+ */
+ onARchitectWorldFailedLaunchingCallback : function(err)
+ {
+ app.report('ARchitect World failed launching');
+ },
+
+ /**
+ *
+ * This function gets called when the Wikitude SDK is ready to start an ARchitect World (the current device is supported by the Wikitude SDK)
+ *
+ */
+ deviceready: function() {
+ // note that this is an event handler so the scope is that of the event
+ // so we need to call app.report(), and not this.report()
+ app.report('deviceready');
+
+ // When PhoneGap finished loading we forward this event into the Wikitude SDK wrapper.
+ // @param {function} A function which gets called if the device is able to launch ARchitect Worlds
+ // @param {function} A function which gets called if the device is not able to launch ARchitect Worlds
+ WikitudePlugin.isDeviceSupported(app.onDeviceIsReadyCallback, app.onDeviceIsUnsupportedCallback);
+
+ // set a callback on the WikitudePlugin to get informed when the ARchitect World finished loading
+ WikitudePlugin.onARchitectWorldLaunchedCallback = app.onARchitectWorldLaunchedCallback;
+
+ // Set a callback on the WikitudePlugin to get informed when the ARchitect World failed loading
+ WikitudePlugin.onARchitectWorldFailedLaunchingCallback = app.onARchitectWorldFailedLaunchingCallback;
+ },
+ report: function(id) {
+ console.log("report:" + id);
+ }
+};
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_128.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_128.png
new file mode 100644
index 0000000..3516df3
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_128.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_16.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_16.png
new file mode 100644
index 0000000..54e19c5
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_16.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_24.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_24.png
new file mode 100644
index 0000000..c7d43ad
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_24.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_256.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_256.png
new file mode 100644
index 0000000..e1cd0e6
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_256.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_32.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_32.png
new file mode 100644
index 0000000..734fffc
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_32.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_48.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_48.png
new file mode 100644
index 0000000..8ad8bac
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_48.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_512.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_512.png
new file mode 100644
index 0000000..c9465f3
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_512.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_64.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_64.png
new file mode 100644
index 0000000..03b3849
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_64.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_android_36.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_android_36.png
new file mode 100644
index 0000000..cd5032a
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_android_36.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_android_48.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_android_48.png
new file mode 100644
index 0000000..e79c606
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_android_48.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_android_72.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_android_72.png
new file mode 100644
index 0000000..4d27634
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_android_72.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_android_96.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_android_96.png
new file mode 100644
index 0000000..ec7ffbf
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_android_96.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_bb_80.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_bb_80.png
new file mode 100644
index 0000000..f86a27a
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_bb_80.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_ios_114.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_ios_114.png
new file mode 100644
index 0000000..efd9c37
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_ios_114.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_ios_144.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_ios_144.png
new file mode 100644
index 0000000..dd819da
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_ios_144.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_ios_57.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_ios_57.png
new file mode 100644
index 0000000..c795fc4
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_ios_57.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_ios_72.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_ios_72.png
new file mode 100644
index 0000000..b1cfde7
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/icon/cordova_ios_72.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_hdpi_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_hdpi_landscape.png
new file mode 100644
index 0000000..a61e2b1
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_hdpi_landscape.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_hdpi_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_hdpi_portrait.png
new file mode 100644
index 0000000..5d6a28a
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_hdpi_portrait.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_ldpi_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_ldpi_landscape.png
new file mode 100644
index 0000000..f3934cd
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_ldpi_landscape.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_ldpi_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_ldpi_portrait.png
new file mode 100644
index 0000000..65ad163
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_ldpi_portrait.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_mdpi_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_mdpi_landscape.png
new file mode 100644
index 0000000..a1b697c
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_mdpi_landscape.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_mdpi_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_mdpi_portrait.png
new file mode 100644
index 0000000..ea15693
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_mdpi_portrait.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_xhdpi_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_xhdpi_landscape.png
new file mode 100644
index 0000000..79f2f09
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_xhdpi_landscape.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_xhdpi_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_xhdpi_portrait.png
new file mode 100644
index 0000000..c2e8042
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/android_xhdpi_portrait.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/blackberry_transparent_300.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/blackberry_transparent_300.png
new file mode 100644
index 0000000..b548bdc
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/blackberry_transparent_300.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/blackberry_transparent_400.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/blackberry_transparent_400.png
new file mode 100644
index 0000000..3facdf9
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/blackberry_transparent_400.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/ipad_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/ipad_landscape.png
new file mode 100644
index 0000000..04be5ac
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/ipad_landscape.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/ipad_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/ipad_portrait.png
new file mode 100644
index 0000000..41e839d
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/ipad_portrait.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/ipad_retina_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/ipad_retina_landscape.png
new file mode 100644
index 0000000..95c542d
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/ipad_retina_landscape.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/ipad_retina_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/ipad_retina_portrait.png
new file mode 100644
index 0000000..aae1862
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/ipad_retina_portrait.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/iphone_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/iphone_landscape.png
new file mode 100644
index 0000000..d154883
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/iphone_landscape.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/iphone_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/iphone_portrait.png
new file mode 100644
index 0000000..6fcba56
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/iphone_portrait.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/iphone_retina_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/iphone_retina_landscape.png
new file mode 100644
index 0000000..0165669
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/iphone_retina_landscape.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/iphone_retina_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/iphone_retina_portrait.png
new file mode 100644
index 0000000..bd24886
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/iphone_retina_portrait.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/windows_phone_portrait.jpg b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/windows_phone_portrait.jpg
new file mode 100644
index 0000000..9f95387
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/res/screen/windows_phone_portrait.jpg differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/spec.html b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/spec.html
new file mode 100644
index 0000000..83d7d2e
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/spec.html
@@ -0,0 +1,50 @@
+
+
+
+ Jasmine Spec Runner
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/spec/helper.js b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/spec/helper.js
new file mode 100644
index 0000000..9f99445
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/spec/helper.js
@@ -0,0 +1,11 @@
+afterEach(function() {
+ document.getElementById('stage').innerHTML = '';
+});
+
+var helper = {
+ trigger: function(obj, name) {
+ var e = document.createEvent('Event');
+ e.initEvent(name, true, true);
+ obj.dispatchEvent(e);
+ }
+};
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/spec/index.js b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/spec/index.js
new file mode 100644
index 0000000..121cf63
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/assets/www/spec/index.js
@@ -0,0 +1,49 @@
+describe('app', function() {
+ describe('initialize', function() {
+ it('should bind deviceready', function() {
+ runs(function() {
+ spyOn(app, 'deviceready');
+ app.initialize();
+ helper.trigger(window.document, 'deviceready');
+ });
+
+ waitsFor(function() {
+ return (app.deviceready.calls.length > 0);
+ }, 'deviceready should be called once', 500);
+
+ runs(function() {
+ expect(app.deviceready).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('deviceready', function() {
+ it('should report that it fired', function() {
+ spyOn(app, 'report');
+ app.deviceready();
+ expect(app.report).toHaveBeenCalledWith('deviceready');
+ });
+ });
+
+ describe('report', function() {
+ beforeEach(function() {
+ var el = document.getElementById('stage');
+ el.innerHTML = ['
',
+ '
Pending
',
+ '
Complete
',
+ '
'].join('\n');
+ });
+
+ it('should show the completion state', function() {
+ app.report('deviceready');
+ var el = document.querySelector('#deviceready .complete:not(.hide)');
+ expect(el).toBeTruthy();
+ });
+
+ it('should hide the pending state', function() {
+ app.report('deviceready');
+ var el = document.querySelector('#deviceready .pending.hide');
+ expect(el).toBeTruthy();
+ });
+ });
+});
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/BOOM b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/BOOM
new file mode 100644
index 0000000..37c623c
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/BOOM
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -e
+
+CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
+
+bash $CORDOVA_PATH/cordova BOOM
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/appinfo.jar b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/appinfo.jar
new file mode 100644
index 0000000..37e00df
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/appinfo.jar differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/clean b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/clean
new file mode 100644
index 0000000..daa8442
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/clean
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -e
+
+CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
+
+bash $CORDOVA_PATH/cordova clean
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/cordova b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/cordova
new file mode 100644
index 0000000..0bca03f
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/cordova
@@ -0,0 +1,85 @@
+#!/bin/bash
+
+set -e
+
+PROJECT_PATH=$( cd "$( dirname "$0" )/.." && pwd )
+
+function check_devices {
+ local devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}'`
+ if [ -z "$devices" ] ; then
+ echo "1"
+ else
+ echo "0"
+ fi
+}
+
+function emulate {
+ declare -a avd_list=($(android list avd | grep "Name:" | cut -f 2 -d ":" | xargs))
+ # we need to start adb-server
+ adb start-server 1>/dev/null
+
+ # Do not launch an emulator if there is already one running or if a device is attached
+ if [ $(check_devices) == 0 ] ; then
+ echo "Device attached or emulator already running"
+ return
+ fi
+
+ local avd_id="1000" #FIXME: hopefully user does not have 1000 AVDs
+ # User has no AVDs
+ if [ ${#avd_list[@]} == 0 ]
+ then
+ echo "You don't have any Android Virtual Devices. Please create at least one AVD."
+ echo "android"
+ fi
+ # User has only one AVD
+ if [ ${#avd_list[@]} == 1 ]
+ then
+ emulator -cpu-delay 0 -no-boot-anim -cache /tmp/cache -avd ${avd_list[0]} 1> /dev/null 2>&1 &
+ # User has more than 1 AVD
+ elif [ ${#avd_list[@]} -gt 1 ]
+ then
+ while [ -z ${avd_list[$avd_id]} ]
+ do
+ echo "Choose from one of the following Android Virtual Devices [0 to $((${#avd_list[@]}-1))]:"
+ for(( i = 0 ; i < ${#avd_list[@]} ; i++ ))
+ do
+ echo "$i) ${avd_list[$i]}"
+ done
+ echo -n "> "
+ read avd_id
+ done
+ emulator -cpu-delay 0 -no-boot-anim -cache /tmp/cache -avd ${avd_list[$avd_id]} 1> /dev/null 2>&1 &
+ fi
+
+}
+
+function clean {
+ ant clean
+}
+# has to be used independently and not in conjuction with other commands
+function log {
+ adb logcat
+}
+
+function debug {
+ if [ $(check_devices) == 0 ] ; then
+ ant debug install
+ else
+ ant debug
+ echo "##################################################################"
+ echo "# Plug in your device or launch an emulator with cordova/emulate #"
+ echo "##################################################################"
+ fi
+}
+
+function launch {
+ local launch_str=$(java -jar $PROJECT_PATH/cordova/appinfo.jar $PROJECT_PATH/AndroidManifest.xml)
+ adb shell am start -n $launch_str
+}
+
+function BOOM {
+ clean && debug && launch
+}
+
+# TODO parse arguments
+(cd $PROJECT_PATH && $1)
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/debug b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/debug
new file mode 100644
index 0000000..5d63a39
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/debug
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -e
+
+CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
+
+bash $CORDOVA_PATH/cordova debug
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/emulate b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/emulate
new file mode 100644
index 0000000..6c4fab2
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/emulate
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -e
+
+CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
+
+bash $CORDOVA_PATH/cordova emulate
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/log b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/log
new file mode 100644
index 0000000..ab3622e
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/cordova/log
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -e
+
+PROJECT_PATH=$( cd "$( dirname "$0" )/.." && pwd )
+
+bash $PROJECT_PATH/cordova/cordova log
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/libs/cordova-2.0.0.jar b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/libs/cordova-2.0.0.jar
new file mode 100644
index 0000000..e01123b
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/libs/cordova-2.0.0.jar differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/proguard-project.txt b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/project.properties b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/project.properties
new file mode 100644
index 0000000..0840b4a
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-15
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/drawable-hdpi/ic_launcher.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/drawable-hdpi/ic_launcher.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/drawable-ldpi/ic_launcher.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..9923872
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/drawable-ldpi/ic_launcher.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/drawable-mdpi/ic_launcher.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/drawable-mdpi/ic_launcher.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/drawable-xhdpi/ic_launcher.png b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/layout/main.xml b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/layout/main.xml
new file mode 100644
index 0000000..991ac56
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/layout/main.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/values/strings.xml b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/values/strings.xml
new file mode 100644
index 0000000..de42976
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+
+
+ Hello World, PhonegapSampleActivity!
+ Hello World
+
+
\ No newline at end of file
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/xml/config.xml b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/xml/config.xml
new file mode 100644
index 0000000..13694f4
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/res/xml/config.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/src/com/wikitude/phonegap/PhonegapSampleActivity.java b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/src/com/wikitude/phonegap/PhonegapSampleActivity.java
new file mode 100644
index 0000000..435e9e4
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/src/com/wikitude/phonegap/PhonegapSampleActivity.java
@@ -0,0 +1,17 @@
+package com.wikitude.phonegap;
+
+import org.apache.cordova.DroidGap;
+
+import android.os.Bundle;
+
+
+
+public class PhonegapSampleActivity extends DroidGap {
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate( Bundle savedInstanceState ) {
+ super.onCreate( savedInstanceState );
+ super.loadUrl( "file:///android_asset/www/index.html" );
+ // super.loadUrl( "file:///android_asset/www/demo.html" );
+ }
+}
\ No newline at end of file
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/src/com/wikitude/phonegap/WikitudePlugin.java b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/src/com/wikitude/phonegap/WikitudePlugin.java
new file mode 100644
index 0000000..f88af1d
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/HelloWorld/src/com/wikitude/phonegap/WikitudePlugin.java
@@ -0,0 +1,417 @@
+package com.wikitude.phonegap;
+
+import java.io.IOException;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.app.Activity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.ViewManager;
+
+import com.phonegap.api.Plugin;
+import com.phonegap.api.PluginResult;
+import com.wikitude.architect.ArchitectUrlListener;
+import com.wikitude.architect.ArchitectView;
+import com.wikitude.architect.ArchitectView.ArchitectConfig;
+
+
+
+/**
+ * Basic PhoneGap Wikitude ARchitect Plugin
+ *
+ * @author Wikitude GmbH
+ */
+public class WikitudePlugin extends Plugin implements ArchitectUrlListener {
+
+ /** PhoneGap-root to Android-App-assets folder */
+ private static final String LOCAL_ASSETS_PATH_ROOT = "assets/";
+
+ /* various JSON-Object keys*/
+ private static final String JSON_KEY_APIKEY = "sdkKey";
+ private static final String JSON_KEY_FILE_PATH = "filePath";
+
+ private static final String JSON_KEY_LOCATION_ALTITUDE = "alt";
+ private static final String JSON_KEY_LOCATION_ACCURACY = "acc";
+ private static final String JSON_KEY_LOCATION_LATITUDE = "lat";
+ private static final String JSON_KEY_LOCATION_LONGITUDE = "lon";
+
+ /* static action strings */
+
+ /**
+ * opens architect-view (add to view stack)
+ */
+ private static final String ACTION_OPEN = "open";
+
+ /**
+ * closes architect-view (remove view stack)
+ */
+ private static final String ACTION_CLOSE = "close";
+
+ /**
+ * set visibility of architectView to visible (of present)
+ */
+ private static final String ACTION_SHOW = "show";
+
+ /**
+ * set visibility of architectView to invisible (of present)
+ */
+ private static final String ACTION_HIDE = "hide";
+
+ /**
+ * inject location information
+ */
+ private static final String ACTION_SET_LOCATION = "setLocation";
+
+ /**
+ * callback for uri-invocations
+ */
+ private static final String ACTION_ON_URLINVOKE = "onUrlInvoke";
+
+ /**
+ * life-cycle notification for resume
+ */
+ private static final String ACTION_ON_RESUME = "onResume";
+
+ /**
+ * life-cycle notification for pause
+ */
+ private static final String ACTION_ON_PAUSE = "onPause";
+
+ /**
+ * check if view is on view-stack (no matter if visible or not)
+ */
+ private static final String ACTION_STATE_ISOPEN = "isOpen";
+
+ /**
+ * opens architect-view (add to view stack)
+ */
+ private static final String ACTION_IS_DEVICE_SUPPORTED = "isDeviceSupported";
+
+ /**
+ * check if view is on view-stack (no matter if visible or not)
+ */
+ private static final String ACTION_CALL_JAVASCRIPT = "callJavascript";
+
+ /**
+ * the Wikitude ARchitectview
+ */
+ private ArchitectView architectView;
+
+ /**
+ * callback-Id of url-invocation method
+ */
+ private String urlInvokeCallbackId = null;
+
+ /**
+ * callback-id of "open"-action method
+ */
+ private String openCallbackId = null;
+
+ @Override
+ public PluginResult execute( final String action, final JSONArray args, final String callbackId ) {
+
+ /* hide architect-view -> destroy and remove from activity */
+ if ( WikitudePlugin.ACTION_CLOSE.equals( action ) ) {
+ if ( this.architectView != null ) {
+ this.cordova.getActivity().runOnUiThread( new Runnable() {
+
+ @Override
+ public void run() {
+ removeArchitectView();
+ }
+ } );
+ return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" );
+ }
+ else {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" );
+ }
+ }
+
+ /* return success only if view is opened (no matter if visible or not) */
+ if ( WikitudePlugin.ACTION_STATE_ISOPEN.equals( action ) ) {
+ if ( this.architectView != null ) {
+ return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" );
+ } else {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" );
+ }
+ }
+
+ /* return success only if view is opened (no matter if visible or not) */
+ if ( WikitudePlugin.ACTION_IS_DEVICE_SUPPORTED.equals( action ) ) {
+ if ( ArchitectView.isDeviceSupported( this.cordova.getActivity() ) ) {
+ return new PluginResult( PluginResult.Status.OK, action + ": this device is ARchitect-ready" );
+ } else {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": Sorry, this device is NOT ARchitect-ready" );
+ }
+ }
+
+
+
+ /* life-cycle's RESUME */
+ if ( WikitudePlugin.ACTION_ON_RESUME.equals( action ) ) {
+
+ if ( this.architectView != null ) {
+ this.cordova.getActivity().runOnUiThread( new Runnable() {
+
+ @Override
+ public void run() {
+ WikitudePlugin.this.architectView.onResume();
+ }
+ } );
+
+ return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" );
+ }
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" );
+ }
+
+ /* life-cycle's PAUSE */
+ if ( WikitudePlugin.ACTION_ON_PAUSE.equals( action ) ) {
+ if ( architectView != null ) {
+ this.cordova.getActivity().runOnUiThread( new Runnable() {
+
+ @Override
+ public void run() {
+ WikitudePlugin.this.architectView.onPause();
+ }
+ } );
+
+ return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" );
+ }
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" );
+ }
+
+ /* set visibility to "visible", return error if view is null */
+ if ( WikitudePlugin.ACTION_SHOW.equals( action ) ) {
+
+ if ( this.architectView != null ) {
+ this.architectView.setVisibility( View.VISIBLE );
+ return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" );
+ } else {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" );
+ }
+ }
+
+ /* set visibility to "invisible", return error if view is null */
+ if ( WikitudePlugin.ACTION_HIDE.equals( action ) ) {
+
+ if ( this.architectView != null ) {
+ this.architectView.setVisibility( View.INVISIBLE );
+ return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" );
+ } else {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" );
+ }
+ }
+
+ /* define call-back for url-invocations */
+ if ( WikitudePlugin.ACTION_ON_URLINVOKE.equals( action ) ) {
+ this.urlInvokeCallbackId = callbackId;
+ PluginResult result = new PluginResult( PluginResult.Status.NO_RESULT, action + ": registered callback" );
+ result.setKeepCallback( true );
+ return result;
+ }
+
+ /* location update */
+ if ( WikitudePlugin.ACTION_SET_LOCATION.equals( action ) ) {
+ if ( this.architectView != null ) {
+ try {
+ String arrStr = args.getString( 0 );
+ JSONObject arr = new JSONObject( arrStr );
+ final double lat = arr.getDouble( WikitudePlugin.JSON_KEY_LOCATION_LATITUDE );
+ final double lon = arr.getDouble( WikitudePlugin.JSON_KEY_LOCATION_LONGITUDE );
+ Object altObj = arr.get( WikitudePlugin.JSON_KEY_LOCATION_ALTITUDE );
+ float alt = Float.MIN_VALUE;
+
+ if ( altObj != null && altObj instanceof Double ) {
+ alt = ((Double)altObj).floatValue();
+ }
+
+ final float altitude = alt;
+
+ final Double acc = arr.getDouble( WikitudePlugin.JSON_KEY_LOCATION_ACCURACY );
+ if ( this.cordova != null && this.cordova.getActivity() != null ) {
+ this.cordova.getActivity().runOnUiThread( new Runnable() {
+
+ @Override
+ public void run() {
+ if ( acc != null ) {
+ WikitudePlugin.this.architectView.setLocation( lat, lon, altitude, acc.floatValue() );
+ } else {
+ WikitudePlugin.this.architectView.setLocation( lat, lon, altitude );
+ }
+ }
+ } );
+ }
+
+ } catch ( Exception e ) {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": exception thrown, " + e != null ? e.getMessage() : "(exception is NULL)" );
+ }
+ return new PluginResult( PluginResult.Status.OK, action + ": updated location" );
+ }
+
+ /* return error if there is no architect-view active*/
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectview is not active" );
+ }
+
+ if ( WikitudePlugin.ACTION_CALL_JAVASCRIPT.equals( action ) ) {
+ if ( this.architectView != null ) {
+ String logMsg = null;
+ try {
+ final String callJS = args.getString( 0 );
+ logMsg = callJS;
+ this.cordova.getActivity().runOnUiThread( new Runnable() {
+
+ @Override
+ public void run() {
+ WikitudePlugin.this.architectView.callJavascript( callJS );
+ }
+ } );
+
+ } catch ( JSONException je ) {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": exception thrown, " + je != null ? je.getMessage() : "(exception is NULL)" );
+ }
+ return new PluginResult( PluginResult.Status.OK, action + ": called js, '" + logMsg + "'" );
+ } else {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectview is not active" );
+ }
+ }
+
+
+ /* initial set-up, show ArchitectView full-screen in current screen/activity */
+ if ( WikitudePlugin.ACTION_OPEN.equals( action ) ) {
+ this.openCallbackId = callbackId;
+ PluginResult result = null;
+ String arrStr = null;
+
+ try {
+ arrStr = args.getString( 0 );
+
+ // arrStr = arrStr.substring( 1, arrStr.length() - 1 );
+
+ JSONObject arr = new JSONObject( arrStr );
+
+ final String apiKey = arr.getString( WikitudePlugin.JSON_KEY_APIKEY );
+ final String filePath = arr.getString( WikitudePlugin.JSON_KEY_FILE_PATH );
+
+ this.cordova.getActivity().runOnUiThread( new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ WikitudePlugin.this.addArchitectView( apiKey, filePath );
+
+ /* call success method once architectView was added successfully */
+ if ( WikitudePlugin.this.openCallbackId != null ) {
+ PluginResult result = new PluginResult( PluginResult.Status.OK );
+ result.setKeepCallback( false );
+ WikitudePlugin.this.success( result, WikitudePlugin.this.openCallbackId );
+ }
+ } catch ( Exception e ) {
+ /* in case "addArchitectView" threw an exception -> notify callback method asynchronously */
+ WikitudePlugin.this.error( e != null ? e.getMessage() : "Exception is 'null'", WikitudePlugin.this.openCallbackId );
+ }
+ }
+ } );
+
+ } catch ( Exception e ) {
+ result = new PluginResult( PluginResult.Status.ERROR, action + ": exception thown, " + e != null ? e.getMessage() : "(exception is NULL)" );
+ result.setKeepCallback( false );
+ return result;
+ }
+
+ /* adding architect-view is done in separate thread, ensure to setKeepCallback so one can call success-method properly later on */
+ result = new PluginResult( PluginResult.Status.NO_RESULT, action + ": no result required, just registered callback-method" );
+ result.setKeepCallback( true );
+
+ return result;
+ }
+
+ /* fall-back return value */
+ return new PluginResult( PluginResult.Status.ERROR, "no such action: " + action );
+ }
+
+ /**
+ * called when url was invoked in architectView (by e.g. calling document.location = "myprotocoll://foo";
+ * @param url the invoked url (e.g. "myprotocoll://foo")
+ * @return true if call was handled properly
+ */
+ @Override
+ public boolean urlWasInvoked( String url ) {
+
+ /* call callback-method if set*/
+ if ( this.urlInvokeCallbackId != null ) {
+ try {
+ /* pass called url as String to callback-method */
+ PluginResult res = new PluginResult( PluginResult.Status.OK, url );
+ res.setKeepCallback( true );
+ this.success( res, this.urlInvokeCallbackId );
+ return true;
+ } catch ( Exception e ) {
+ this.error( "invalid url invoked: " + url, this.urlInvokeCallbackId );
+ }
+ }
+ return false;
+ }
+
+ /**
+ * hides/removes ARchitect-View completely
+ * @return true if successful, false otherwise
+ */
+ private boolean removeArchitectView() {
+ if ( this.architectView != null ) {
+ /* fake life-cycle calls, because activity is already up and running */
+ this.architectView.onPause();
+ this.architectView.onDestroy();
+ this.architectView.setVisibility( View.INVISIBLE );
+ ((ViewManager)this.architectView.getParent()).removeView( this.architectView );
+ this.architectView = null;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Architect-Configuration required for proper set-up
+ * @param apiKey
+ * @return
+ */
+ protected ArchitectConfig getArchitectConfig( final String apiKey ) {
+ /* no special set-up required in default Wikitude-Plugin, further things required in advanced usage (e.g. Vuforia Image Recognition) */
+ return new ArchitectConfig( apiKey );
+ }
+
+ /**
+ * add architectView to current screen
+ * @param apiKey developers's api key to use (hides watermarking/intro-animation if it matches your package-name)
+ * @param filePath the url (starting with http:// for online use; starting with LOCAL_ASSETS_PATH_ROOT if oyu want to load assets within your app-assets folder)
+ * @throws IOException might be thrown from ARchitect-SDK
+ */
+ @SuppressWarnings("deprecation")
+ private void addArchitectView( final String apiKey, String filePath ) throws IOException {
+ if ( this.architectView == null ) {
+ this.architectView = new ArchitectView( (Activity)this.ctx.getContext() );
+
+ /* add content view and fake initial life-cycle */
+ ((Activity)this.ctx.getContext()).addContentView( this.architectView, new ViewGroup.LayoutParams( LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT ) );
+
+ /* fake life-cycle calls, because activity is already up and running */
+ this.architectView.onCreate( getArchitectConfig( apiKey ) );
+ this.architectView.onPostCreate();
+
+ /* register self as url listener to fwd these native calls to PhoneGap */
+ this.architectView.registerUrlListener( WikitudePlugin.this );
+
+ /* load asset from local directory if prefix is used */
+ if ( filePath.startsWith( WikitudePlugin.LOCAL_ASSETS_PATH_ROOT ) ) {
+ filePath = filePath.substring( WikitudePlugin.LOCAL_ASSETS_PATH_ROOT.length() );
+ }
+ this.architectView.load( filePath );
+
+ /* also a fake-life-cycle call (the last one before it is really shown in UI */
+ this.architectView.onResume();
+ }
+ }
+}
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Basic/README.md b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/README.md
new file mode 100644
index 0000000..e8ccd3a
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Basic/README.md
@@ -0,0 +1,14 @@
+# Hello World
+This sample dispalys Hello world in the camera
+The project is a stand-alone Sample PhoneGap Android Project using the basic Wikitude PhoneGap plugin to display "Hello World" in the camera-view. Solely the wikitudesdk.jar needs to be pasted to the project and [added to build path](https://www.google.com/webhp?sourceid=chrome-instant&ie=UTF-8&ion=1#hl=de&sclient=psy-ab&q=how+to+add+library+to+build+path+in+eclipse&oq=how+to+add+library+to+build+path+in+eclipse&gs_l=serp.3...4768.4984.1.5346.3.3.0.0.0.1.116.271.2j1.3.0...0.0...1c.1.Nk4mv2tTjuA&pbx=1&bav=on.2,or.r_gc.r_pw.r_cp.r_qf.&fp=a6ff543f77bf189e&ion=1&biw=1236&bih=661)
+ to get rid of the compile-time errors
+
+####Prerequisites:
+* You need to [register as developer at Wikitude](http://developer.wikitude.com) and downloaded the SDK
+# SETUP
+
+
+* [Import it in Eclipse as Android-Project](https://www.google.com/webhp?sourceid=chrome-instant&ie=UTF-8&ion=1#hl=de&output=search&sclient=psy-ab&q=import%20android%20project%20into%20eclipse&oq=&gs_l=&pbx=1&fp=531bf0abdc9ea0e7&ion=1&bav=on.2,or.r_gc.r_pw.r_cp.r_qf.&biw=1030&bih=550)
+* Copy `wikitudesdk.jar` from the Android [WikitudeSDK](http://developer.wikitude.com) to project's `libs`-folder
+* Run the application on your device / emulator
+
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/.classpath b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/.classpath
new file mode 100644
index 0000000..36bd4cc
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/.classpath
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/.project b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/.project
new file mode 100644
index 0000000..a0515ec
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/.project
@@ -0,0 +1,33 @@
+
+
+ HelloImageRecognition
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/AndroidManifest.xml b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/AndroidManifest.xml
new file mode 100644
index 0000000..159c389
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/AndroidManifest.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/DirectionArrow.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/DirectionArrow.png
new file mode 100644
index 0000000..8a0ccb6
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/DirectionArrow.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/SimpleIRWorld.html b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/SimpleIRWorld.html
new file mode 100644
index 0000000..4b23026
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/SimpleIRWorld.html
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+Simple IR World
+
+
+
+
+
+
+
+
+
+
+
+
+
Loading ...
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/WikitudeLogo.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/WikitudeLogo.png
new file mode 100644
index 0000000..f820ca0
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/WikitudeLogo.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/WikitudeLogo.zip b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/WikitudeLogo.zip
new file mode 100644
index 0000000..34db5dc
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/WikitudeLogo.zip differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/overlay.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/overlay.png
new file mode 100644
index 0000000..5d2dfcd
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/world/SimpleImageRecognition/overlay.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/cordova-2.0.0.js b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/cordova-2.0.0.js
new file mode 100644
index 0000000..ba9e6a9
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/cordova-2.0.0.js
@@ -0,0 +1,5724 @@
+// commit 114cf5304a74ff8f7c9ff1d21cf5652298af04b0
+
+// File generated at :: Wed Jul 18 2012 14:44:33 GMT-0700 (PDT)
+
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+;(function() {
+
+// file: lib/scripts/require.js
+var require,
+ define;
+
+(function () {
+ var modules = {};
+
+ function build(module) {
+ var factory = module.factory;
+ module.exports = {};
+ delete module.factory;
+ factory(require, module.exports, module);
+ return module.exports;
+ }
+
+ require = function (id) {
+ if (!modules[id]) {
+ throw "module " + id + " not found";
+ }
+ return modules[id].factory ? build(modules[id]) : modules[id].exports;
+ };
+
+ define = function (id, factory) {
+ if (modules[id]) {
+ throw "module " + id + " already defined";
+ }
+
+ modules[id] = {
+ id: id,
+ factory: factory
+ };
+ };
+
+ define.remove = function (id) {
+ delete modules[id];
+ };
+
+})();
+
+//Export for use in node
+if (typeof module === "object" && typeof require === "function") {
+ module.exports.require = require;
+ module.exports.define = define;
+}
+// file: lib/cordova.js
+define("cordova", function(require, exports, module) {
+var channel = require('cordova/channel');
+
+/**
+ * Listen for DOMContentLoaded and notify our channel subscribers.
+ */
+document.addEventListener('DOMContentLoaded', function() {
+ channel.onDOMContentLoaded.fire();
+}, false);
+if (document.readyState == 'complete' || document.readyState == 'interactive') {
+ channel.onDOMContentLoaded.fire();
+}
+
+/**
+ * Intercept calls to addEventListener + removeEventListener and handle deviceready,
+ * resume, and pause events.
+ */
+var m_document_addEventListener = document.addEventListener;
+var m_document_removeEventListener = document.removeEventListener;
+var m_window_addEventListener = window.addEventListener;
+var m_window_removeEventListener = window.removeEventListener;
+
+/**
+ * Houses custom event handlers to intercept on document + window event listeners.
+ */
+var documentEventHandlers = {},
+ windowEventHandlers = {};
+
+document.addEventListener = function(evt, handler, capture) {
+ var e = evt.toLowerCase();
+ if (typeof documentEventHandlers[e] != 'undefined') {
+ if (evt === 'deviceready') {
+ documentEventHandlers[e].subscribeOnce(handler);
+ } else {
+ documentEventHandlers[e].subscribe(handler);
+ }
+ } else {
+ m_document_addEventListener.call(document, evt, handler, capture);
+ }
+};
+
+window.addEventListener = function(evt, handler, capture) {
+ var e = evt.toLowerCase();
+ if (typeof windowEventHandlers[e] != 'undefined') {
+ windowEventHandlers[e].subscribe(handler);
+ } else {
+ m_window_addEventListener.call(window, evt, handler, capture);
+ }
+};
+
+document.removeEventListener = function(evt, handler, capture) {
+ var e = evt.toLowerCase();
+ // If unsubcribing from an event that is handled by a plugin
+ if (typeof documentEventHandlers[e] != "undefined") {
+ documentEventHandlers[e].unsubscribe(handler);
+ } else {
+ m_document_removeEventListener.call(document, evt, handler, capture);
+ }
+};
+
+window.removeEventListener = function(evt, handler, capture) {
+ var e = evt.toLowerCase();
+ // If unsubcribing from an event that is handled by a plugin
+ if (typeof windowEventHandlers[e] != "undefined") {
+ windowEventHandlers[e].unsubscribe(handler);
+ } else {
+ m_window_removeEventListener.call(window, evt, handler, capture);
+ }
+};
+
+function createEvent(type, data) {
+ var event = document.createEvent('Events');
+ event.initEvent(type, false, false);
+ if (data) {
+ for (var i in data) {
+ if (data.hasOwnProperty(i)) {
+ event[i] = data[i];
+ }
+ }
+ }
+ return event;
+}
+
+if(typeof window.console === "undefined") {
+ window.console = {
+ log:function(){}
+ };
+}
+
+var cordova = {
+ define:define,
+ require:require,
+ /**
+ * Methods to add/remove your own addEventListener hijacking on document + window.
+ */
+ addWindowEventHandler:function(event, opts) {
+ return (windowEventHandlers[event] = channel.create(event, opts));
+ },
+ addDocumentEventHandler:function(event, opts) {
+ return (documentEventHandlers[event] = channel.create(event, opts));
+ },
+ removeWindowEventHandler:function(event) {
+ delete windowEventHandlers[event];
+ },
+ removeDocumentEventHandler:function(event) {
+ delete documentEventHandlers[event];
+ },
+ /**
+ * Retreive original event handlers that were replaced by Cordova
+ *
+ * @return object
+ */
+ getOriginalHandlers: function() {
+ return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener},
+ 'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}};
+ },
+ /**
+ * Method to fire event from native code
+ */
+ fireDocumentEvent: function(type, data) {
+ var evt = createEvent(type, data);
+ if (typeof documentEventHandlers[type] != 'undefined') {
+ setTimeout(function() {
+ documentEventHandlers[type].fire(evt);
+ }, 0);
+ } else {
+ document.dispatchEvent(evt);
+ }
+ },
+ fireWindowEvent: function(type, data) {
+ var evt = createEvent(type,data);
+ if (typeof windowEventHandlers[type] != 'undefined') {
+ setTimeout(function() {
+ windowEventHandlers[type].fire(evt);
+ }, 0);
+ } else {
+ window.dispatchEvent(evt);
+ }
+ },
+ // TODO: this is Android only; think about how to do this better
+ shuttingDown:false,
+ UsePolling:false,
+ // END TODO
+
+ // TODO: iOS only
+ // This queue holds the currently executing command and all pending
+ // commands executed with cordova.exec().
+ commandQueue:[],
+ // Indicates if we're currently in the middle of flushing the command
+ // queue on the native side.
+ commandQueueFlushing:false,
+ // END TODO
+ /**
+ * Plugin callback mechanism.
+ */
+ callbackId: 0,
+ callbacks: {},
+ callbackStatus: {
+ NO_RESULT: 0,
+ OK: 1,
+ CLASS_NOT_FOUND_EXCEPTION: 2,
+ ILLEGAL_ACCESS_EXCEPTION: 3,
+ INSTANTIATION_EXCEPTION: 4,
+ MALFORMED_URL_EXCEPTION: 5,
+ IO_EXCEPTION: 6,
+ INVALID_ACTION: 7,
+ JSON_EXCEPTION: 8,
+ ERROR: 9
+ },
+
+ /**
+ * Called by native code when returning successful result from an action.
+ *
+ * @param callbackId
+ * @param args
+ */
+ callbackSuccess: function(callbackId, args) {
+ if (cordova.callbacks[callbackId]) {
+
+ // If result is to be sent to callback
+ if (args.status == cordova.callbackStatus.OK) {
+ try {
+ if (cordova.callbacks[callbackId].success) {
+ cordova.callbacks[callbackId].success(args.message);
+ }
+ }
+ catch (e) {
+ console.log("Error in success callback: "+callbackId+" = "+e);
+ }
+ }
+
+ // Clear callback if not expecting any more results
+ if (!args.keepCallback) {
+ delete cordova.callbacks[callbackId];
+ }
+ }
+ },
+
+ /**
+ * Called by native code when returning error result from an action.
+ *
+ * @param callbackId
+ * @param args
+ */
+ callbackError: function(callbackId, args) {
+ if (cordova.callbacks[callbackId]) {
+ try {
+ if (cordova.callbacks[callbackId].fail) {
+ cordova.callbacks[callbackId].fail(args.message);
+ }
+ }
+ catch (e) {
+ console.log("Error in error callback: "+callbackId+" = "+e);
+ }
+
+ // Clear callback if not expecting any more results
+ if (!args.keepCallback) {
+ delete cordova.callbacks[callbackId];
+ }
+ }
+ },
+ addConstructor: function(func) {
+ channel.onCordovaReady.subscribeOnce(function() {
+ try {
+ func();
+ } catch(e) {
+ console.log("Failed to run constructor: " + e);
+ }
+ });
+ }
+};
+
+// Register pause, resume and deviceready channels as events on document.
+channel.onPause = cordova.addDocumentEventHandler('pause');
+channel.onResume = cordova.addDocumentEventHandler('resume');
+channel.onDeviceReady = cordova.addDocumentEventHandler('deviceready');
+
+module.exports = cordova;
+
+});
+
+// file: lib/common/builder.js
+define("cordova/builder", function(require, exports, module) {
+var utils = require('cordova/utils');
+
+function each(objects, func, context) {
+ for (var prop in objects) {
+ if (objects.hasOwnProperty(prop)) {
+ func.apply(context, [objects[prop], prop]);
+ }
+ }
+}
+
+function include(parent, objects, clobber, merge) {
+ each(objects, function (obj, key) {
+ try {
+ var result = obj.path ? require(obj.path) : {};
+
+ if (clobber) {
+ // Clobber if it doesn't exist.
+ if (typeof parent[key] === 'undefined') {
+ parent[key] = result;
+ } else if (typeof obj.path !== 'undefined') {
+ // If merging, merge properties onto parent, otherwise, clobber.
+ if (merge) {
+ recursiveMerge(parent[key], result);
+ } else {
+ parent[key] = result;
+ }
+ }
+ result = parent[key];
+ } else {
+ // Overwrite if not currently defined.
+ if (typeof parent[key] == 'undefined') {
+ parent[key] = result;
+ } else if (merge && typeof obj.path !== 'undefined') {
+ // If merging, merge parent onto result
+ recursiveMerge(result, parent[key]);
+ parent[key] = result;
+ } else {
+ // Set result to what already exists, so we can build children into it if they exist.
+ result = parent[key];
+ }
+ }
+
+ if (obj.children) {
+ include(result, obj.children, clobber, merge);
+ }
+ } catch(e) {
+ utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"');
+ }
+ });
+}
+
+/**
+ * Merge properties from one object onto another recursively. Properties from
+ * the src object will overwrite existing target property.
+ *
+ * @param target Object to merge properties into.
+ * @param src Object to merge properties from.
+ */
+function recursiveMerge(target, src) {
+ for (var prop in src) {
+ if (src.hasOwnProperty(prop)) {
+ if (typeof target.prototype !== 'undefined' && target.prototype.constructor === target) {
+ // If the target object is a constructor override off prototype.
+ target.prototype[prop] = src[prop];
+ } else {
+ target[prop] = typeof src[prop] === 'object' ? recursiveMerge(
+ target[prop], src[prop]) : src[prop];
+ }
+ }
+ }
+ return target;
+}
+
+module.exports = {
+ build: function (objects) {
+ return {
+ intoButDontClobber: function (target) {
+ include(target, objects, false, false);
+ },
+ intoAndClobber: function(target) {
+ include(target, objects, true, false);
+ },
+ intoAndMerge: function(target) {
+ include(target, objects, true, true);
+ }
+ };
+ }
+};
+
+});
+
+// file: lib/common/channel.js
+define("cordova/channel", function(require, exports, module) {
+var utils = require('cordova/utils');
+
+/**
+ * Custom pub-sub "channel" that can have functions subscribed to it
+ * This object is used to define and control firing of events for
+ * cordova initialization.
+ *
+ * The order of events during page load and Cordova startup is as follows:
+ *
+ * onDOMContentLoaded Internal event that is received when the web page is loaded and parsed.
+ * onNativeReady Internal event that indicates the Cordova native side is ready.
+ * onCordovaReady Internal event fired when all Cordova JavaScript objects have been created.
+ * onCordovaInfoReady Internal event fired when device properties are available.
+ * onCordovaConnectionReady Internal event fired when the connection property has been set.
+ * onDeviceReady User event fired to indicate that Cordova is ready
+ * onResume User event fired to indicate a start/resume lifecycle event
+ * onPause User event fired to indicate a pause lifecycle event
+ * onDestroy Internal event fired when app is being destroyed (User should use window.onunload event, not this one).
+ *
+ * The only Cordova events that user code should register for are:
+ * deviceready Cordova native code is initialized and Cordova APIs can be called from JavaScript
+ * pause App has moved to background
+ * resume App has returned to foreground
+ *
+ * Listeners can be registered as:
+ * document.addEventListener("deviceready", myDeviceReadyListener, false);
+ * document.addEventListener("resume", myResumeListener, false);
+ * document.addEventListener("pause", myPauseListener, false);
+ *
+ * The DOM lifecycle events should be used for saving and restoring state
+ * window.onload
+ * window.onunload
+ *
+ */
+
+/**
+ * Channel
+ * @constructor
+ * @param type String the channel name
+ * @param opts Object options to pass into the channel, currently
+ * supports:
+ * onSubscribe: callback that fires when
+ * something subscribes to the Channel. Sets
+ * context to the Channel.
+ * onUnsubscribe: callback that fires when
+ * something unsubscribes to the Channel. Sets
+ * context to the Channel.
+ */
+var Channel = function(type, opts) {
+ this.type = type;
+ this.handlers = {};
+ this.numHandlers = 0;
+ this.guid = 1;
+ this.fired = false;
+ this.enabled = true;
+ this.events = {
+ onSubscribe:null,
+ onUnsubscribe:null
+ };
+ if (opts) {
+ if (opts.onSubscribe) this.events.onSubscribe = opts.onSubscribe;
+ if (opts.onUnsubscribe) this.events.onUnsubscribe = opts.onUnsubscribe;
+ }
+},
+ channel = {
+ /**
+ * Calls the provided function only after all of the channels specified
+ * have been fired.
+ */
+ join: function (h, c) {
+ var i = c.length;
+ var len = i;
+ var f = function() {
+ if (!(--i)) h();
+ };
+ for (var j=0; j 0) {
+ var v = JSON.parse(r);
+
+ // If status is OK, then return value back to caller
+ if (v.status === cordova.callbackStatus.OK) {
+
+ // If there is a success callback, then call it now with
+ // returned value
+ if (success) {
+ try {
+ success(v.message);
+ } catch (e) {
+ console.log("Error in success callback: " + callbackId + " = " + e);
+ }
+
+ // Clear callback if not expecting any more results
+ if (!v.keepCallback) {
+ delete cordova.callbacks[callbackId];
+ }
+ }
+ return v.message;
+ }
+
+ // If no result
+ else if (v.status === cordova.callbackStatus.NO_RESULT) {
+ // Clear callback if not expecting any more results
+ if (!v.keepCallback) {
+ delete cordova.callbacks[callbackId];
+ }
+ }
+
+ // If error, then display error
+ else {
+ console.log("Error: Status="+v.status+" Message="+v.message);
+
+ // If there is a fail callback, then call it now with returned value
+ if (fail) {
+ try {
+ fail(v.message);
+ }
+ catch (e1) {
+ console.log("Error in error callback: "+callbackId+" = "+e1);
+ }
+
+ // Clear callback if not expecting any more results
+ if (!v.keepCallback) {
+ delete cordova.callbacks[callbackId];
+ }
+ }
+ return null;
+ }
+ }
+ } catch (e2) {
+ console.log("Error: "+e2);
+ }
+};
+
+});
+
+// file: lib/android/platform.js
+define("cordova/platform", function(require, exports, module) {
+module.exports = {
+ id: "android",
+ initialize:function() {
+ var channel = require("cordova/channel"),
+ cordova = require('cordova'),
+ callback = require('cordova/plugin/android/callback'),
+ polling = require('cordova/plugin/android/polling'),
+ exec = require('cordova/exec');
+
+ channel.onDestroy.subscribe(function() {
+ cordova.shuttingDown = true;
+ });
+
+ // Start listening for XHR callbacks
+ // Figure out which bridge approach will work on this Android
+ // device: polling or XHR-based callbacks
+ setTimeout(function() {
+ if (cordova.UsePolling) {
+ polling();
+ }
+ else {
+ var isPolling = prompt("usePolling", "gap_callbackServer:");
+ cordova.UsePolling = isPolling;
+ if (isPolling == "true") {
+ cordova.UsePolling = true;
+ polling();
+ } else {
+ cordova.UsePolling = false;
+ callback();
+ }
+ }
+ }, 1);
+
+ // Inject a listener for the backbutton on the document.
+ var backButtonChannel = cordova.addDocumentEventHandler('backbutton', {
+ onSubscribe:function() {
+ // If we just attached the first handler, let native know we need to override the back button.
+ if (this.numHandlers === 1) {
+ exec(null, null, "App", "overrideBackbutton", [true]);
+ }
+ },
+ onUnsubscribe:function() {
+ // If we just detached the last handler, let native know we no longer override the back button.
+ if (this.numHandlers === 0) {
+ exec(null, null, "App", "overrideBackbutton", [false]);
+ }
+ }
+ });
+
+ // Add hardware MENU and SEARCH button handlers
+ cordova.addDocumentEventHandler('menubutton');
+ cordova.addDocumentEventHandler('searchbutton');
+
+ // Figure out if we need to shim-in localStorage and WebSQL
+ // support from the native side.
+ var storage = require('cordova/plugin/android/storage');
+
+ // First patch WebSQL if necessary
+ if (typeof window.openDatabase == 'undefined') {
+ // Not defined, create an openDatabase function for all to use!
+ window.openDatabase = storage.openDatabase;
+ } else {
+ // Defined, but some Android devices will throw a SECURITY_ERR -
+ // so we wrap the whole thing in a try-catch and shim in our own
+ // if the device has Android bug 16175.
+ var originalOpenDatabase = window.openDatabase;
+ window.openDatabase = function(name, version, desc, size) {
+ var db = null;
+ try {
+ db = originalOpenDatabase(name, version, desc, size);
+ }
+ catch (ex) {
+ if (ex.code === 18) {
+ db = null;
+ } else {
+ throw ex;
+ }
+ }
+
+ if (db === null) {
+ return storage.openDatabase(name, version, desc, size);
+ }
+ else {
+ return db;
+ }
+
+ };
+ }
+
+ // Patch localStorage if necessary
+ if (typeof window.localStorage == 'undefined' || window.localStorage === null) {
+ window.localStorage = new storage.CupcakeLocalStorage();
+ }
+
+ // Let native code know we are all done on the JS side.
+ // Native code will then un-hide the WebView.
+ channel.join(function() {
+ exec(null, null, "App", "show", []);
+ }, [channel.onCordovaReady]);
+ },
+ objects: {
+ cordova: {
+ children: {
+ JSCallback:{
+ path:"cordova/plugin/android/callback"
+ },
+ JSCallbackPolling:{
+ path:"cordova/plugin/android/polling"
+ }
+ }
+ },
+ navigator: {
+ children: {
+ app:{
+ path: "cordova/plugin/android/app"
+ }
+ }
+ },
+ File: { // exists natively on Android WebView, override
+ path: "cordova/plugin/File"
+ },
+ FileReader: { // exists natively on Android WebView, override
+ path: "cordova/plugin/FileReader"
+ },
+ FileError: { //exists natively on Android WebView on Android 4.x
+ path: "cordova/plugin/FileError"
+ },
+ MediaError: { // exists natively on Android WebView on Android 4.x
+ path: "cordova/plugin/MediaError"
+ }
+ },
+ merges: {
+ device: {
+ path: 'cordova/plugin/android/device'
+ },
+ navigator: {
+ children: {
+ notification: {
+ path: 'cordova/plugin/android/notification'
+ }
+ }
+ }
+ }
+};
+
+});
+
+// file: lib/common/plugin/Acceleration.js
+define("cordova/plugin/Acceleration", function(require, exports, module) {
+var Acceleration = function(x, y, z, timestamp) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.timestamp = timestamp || (new Date()).getTime();
+};
+
+module.exports = Acceleration;
+
+});
+
+// file: lib/common/plugin/Camera.js
+define("cordova/plugin/Camera", function(require, exports, module) {
+var exec = require('cordova/exec'),
+ Camera = require('cordova/plugin/CameraConstants');
+
+var cameraExport = {};
+
+// Tack on the Camera Constants to the base camera plugin.
+for (var key in Camera) {
+ cameraExport[key] = Camera[key];
+}
+
+/**
+ * Gets a picture from source defined by "options.sourceType", and returns the
+ * image as defined by the "options.destinationType" option.
+
+ * The defaults are sourceType=CAMERA and destinationType=FILE_URI.
+ *
+ * @param {Function} successCallback
+ * @param {Function} errorCallback
+ * @param {Object} options
+ */
+cameraExport.getPicture = function(successCallback, errorCallback, options) {
+ // successCallback required
+ if (typeof successCallback != "function") {
+ console.log("Camera Error: successCallback is not a function");
+ return;
+ }
+
+ // errorCallback optional
+ if (errorCallback && (typeof errorCallback != "function")) {
+ console.log("Camera Error: errorCallback is not a function");
+ return;
+ }
+
+ var quality = 50;
+ if (options && typeof options.quality == "number") {
+ quality = options.quality;
+ } else if (options && typeof options.quality == "string") {
+ var qlity = parseInt(options.quality, 10);
+ if (isNaN(qlity) === false) {
+ quality = qlity.valueOf();
+ }
+ }
+
+ var destinationType = Camera.DestinationType.FILE_URI;
+ if (typeof options.destinationType == "number") {
+ destinationType = options.destinationType;
+ }
+
+ var sourceType = Camera.PictureSourceType.CAMERA;
+ if (typeof options.sourceType == "number") {
+ sourceType = options.sourceType;
+ }
+
+ var targetWidth = -1;
+ if (typeof options.targetWidth == "number") {
+ targetWidth = options.targetWidth;
+ } else if (typeof options.targetWidth == "string") {
+ var width = parseInt(options.targetWidth, 10);
+ if (isNaN(width) === false) {
+ targetWidth = width.valueOf();
+ }
+ }
+
+ var targetHeight = -1;
+ if (typeof options.targetHeight == "number") {
+ targetHeight = options.targetHeight;
+ } else if (typeof options.targetHeight == "string") {
+ var height = parseInt(options.targetHeight, 10);
+ if (isNaN(height) === false) {
+ targetHeight = height.valueOf();
+ }
+ }
+
+ var encodingType = Camera.EncodingType.JPEG;
+ if (typeof options.encodingType == "number") {
+ encodingType = options.encodingType;
+ }
+
+ var mediaType = Camera.MediaType.PICTURE;
+ if (typeof options.mediaType == "number") {
+ mediaType = options.mediaType;
+ }
+ var allowEdit = false;
+ if (typeof options.allowEdit == "boolean") {
+ allowEdit = options.allowEdit;
+ } else if (typeof options.allowEdit == "number") {
+ allowEdit = options.allowEdit <= 0 ? false : true;
+ }
+ var correctOrientation = false;
+ if (typeof options.correctOrientation == "boolean") {
+ correctOrientation = options.correctOrientation;
+ } else if (typeof options.correctOrientation == "number") {
+ correctOrientation = options.correctOrientation <=0 ? false : true;
+ }
+ var saveToPhotoAlbum = false;
+ if (typeof options.saveToPhotoAlbum == "boolean") {
+ saveToPhotoAlbum = options.saveToPhotoAlbum;
+ } else if (typeof options.saveToPhotoAlbum == "number") {
+ saveToPhotoAlbum = options.saveToPhotoAlbum <=0 ? false : true;
+ }
+ var popoverOptions = null;
+ if (typeof options.popoverOptions == "object") {
+ popoverOptions = options.popoverOptions;
+ }
+
+ var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
+ mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions];
+
+ exec(successCallback, errorCallback, "Camera", "takePicture", args);
+};
+
+cameraExport.cleanup = function(successCallback, errorCallback) {
+ exec(successCallback, errorCallback, "Camera", "cleanup", []);
+};
+
+module.exports = cameraExport;
+});
+
+// file: lib/common/plugin/CameraConstants.js
+define("cordova/plugin/CameraConstants", function(require, exports, module) {
+module.exports = {
+ DestinationType:{
+ DATA_URL: 0, // Return base64 encoded string
+ FILE_URI: 1 // Return file uri (content://media/external/images/media/2 for Android)
+ },
+ EncodingType:{
+ JPEG: 0, // Return JPEG encoded image
+ PNG: 1 // Return PNG encoded image
+ },
+ MediaType:{
+ PICTURE: 0, // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType
+ VIDEO: 1, // allow selection of video only, ONLY RETURNS URL
+ ALLMEDIA : 2 // allow selection from all media types
+ },
+ PictureSourceType:{
+ PHOTOLIBRARY : 0, // Choose image from picture library (same as SAVEDPHOTOALBUM for Android)
+ CAMERA : 1, // Take picture from camera
+ SAVEDPHOTOALBUM : 2 // Choose image from picture library (same as PHOTOLIBRARY for Android)
+ },
+ PopoverArrowDirection:{
+ ARROW_UP : 1, // matches iOS UIPopoverArrowDirection constants to specify arrow location on popover
+ ARROW_DOWN : 2,
+ ARROW_LEFT : 4,
+ ARROW_RIGHT : 8,
+ ARROW_ANY : 15
+ }
+};
+});
+
+// file: lib/common/plugin/CameraPopoverOptions.js
+define("cordova/plugin/CameraPopoverOptions", function(require, exports, module) {
+var Camera = require('cordova/plugin/CameraConstants');
+
+/**
+ * Encapsulates options for iOS Popover image picker
+ */
+var CameraPopoverOptions = function(x,y,width,height,arrowDir){
+ // information of rectangle that popover should be anchored to
+ this.x = x || 0;
+ this.y = y || 32;
+ this.width = width || 320;
+ this.height = height || 480;
+ // The direction of the popover arrow
+ this.arrowDir = arrowDir || Camera.PopoverArrowDirection.ARROW_ANY;
+};
+
+module.exports = CameraPopoverOptions;
+});
+
+// file: lib/common/plugin/CaptureAudioOptions.js
+define("cordova/plugin/CaptureAudioOptions", function(require, exports, module) {
+/**
+ * Encapsulates all audio capture operation configuration options.
+ */
+var CaptureAudioOptions = function(){
+ // Upper limit of sound clips user can record. Value must be equal or greater than 1.
+ this.limit = 1;
+ // Maximum duration of a single sound clip in seconds.
+ this.duration = 0;
+ // The selected audio mode. Must match with one of the elements in supportedAudioModes array.
+ this.mode = null;
+};
+
+module.exports = CaptureAudioOptions;
+});
+
+// file: lib/common/plugin/CaptureError.js
+define("cordova/plugin/CaptureError", function(require, exports, module) {
+/**
+ * The CaptureError interface encapsulates all errors in the Capture API.
+ */
+var CaptureError = function(c) {
+ this.code = c || null;
+};
+
+// Camera or microphone failed to capture image or sound.
+CaptureError.CAPTURE_INTERNAL_ERR = 0;
+// Camera application or audio capture application is currently serving other capture request.
+CaptureError.CAPTURE_APPLICATION_BUSY = 1;
+// Invalid use of the API (e.g. limit parameter has value less than one).
+CaptureError.CAPTURE_INVALID_ARGUMENT = 2;
+// User exited camera application or audio capture application before capturing anything.
+CaptureError.CAPTURE_NO_MEDIA_FILES = 3;
+// The requested capture operation is not supported.
+CaptureError.CAPTURE_NOT_SUPPORTED = 20;
+
+module.exports = CaptureError;
+});
+
+// file: lib/common/plugin/CaptureImageOptions.js
+define("cordova/plugin/CaptureImageOptions", function(require, exports, module) {
+/**
+ * Encapsulates all image capture operation configuration options.
+ */
+var CaptureImageOptions = function(){
+ // Upper limit of images user can take. Value must be equal or greater than 1.
+ this.limit = 1;
+ // The selected image mode. Must match with one of the elements in supportedImageModes array.
+ this.mode = null;
+};
+
+module.exports = CaptureImageOptions;
+});
+
+// file: lib/common/plugin/CaptureVideoOptions.js
+define("cordova/plugin/CaptureVideoOptions", function(require, exports, module) {
+/**
+ * Encapsulates all video capture operation configuration options.
+ */
+var CaptureVideoOptions = function(){
+ // Upper limit of videos user can record. Value must be equal or greater than 1.
+ this.limit = 1;
+ // Maximum duration of a single video clip in seconds.
+ this.duration = 0;
+ // The selected video mode. Must match with one of the elements in supportedVideoModes array.
+ this.mode = null;
+};
+
+module.exports = CaptureVideoOptions;
+});
+
+// file: lib/common/plugin/CompassError.js
+define("cordova/plugin/CompassError", function(require, exports, module) {
+/**
+ * CompassError.
+ * An error code assigned by an implementation when an error has occured
+ * @constructor
+ */
+var CompassError = function(err) {
+ this.code = (err !== undefined ? err : null);
+};
+
+CompassError.COMPASS_INTERNAL_ERR = 0;
+CompassError.COMPASS_NOT_SUPPORTED = 20;
+
+module.exports = CompassError;
+});
+
+// file: lib/common/plugin/CompassHeading.js
+define("cordova/plugin/CompassHeading", function(require, exports, module) {
+var CompassHeading = function(magneticHeading, trueHeading, headingAccuracy, timestamp) {
+ this.magneticHeading = (magneticHeading !== undefined ? magneticHeading : null);
+ this.trueHeading = (trueHeading !== undefined ? trueHeading : null);
+ this.headingAccuracy = (headingAccuracy !== undefined ? headingAccuracy : null);
+ this.timestamp = (timestamp !== undefined ? timestamp : new Date().getTime());
+};
+
+module.exports = CompassHeading;
+});
+
+// file: lib/common/plugin/ConfigurationData.js
+define("cordova/plugin/ConfigurationData", function(require, exports, module) {
+/**
+ * Encapsulates a set of parameters that the capture device supports.
+ */
+function ConfigurationData() {
+ // The ASCII-encoded string in lower case representing the media type.
+ this.type = null;
+ // The height attribute represents height of the image or video in pixels.
+ // In the case of a sound clip this attribute has value 0.
+ this.height = 0;
+ // The width attribute represents width of the image or video in pixels.
+ // In the case of a sound clip this attribute has value 0
+ this.width = 0;
+}
+
+module.exports = ConfigurationData;
+});
+
+// file: lib/common/plugin/Connection.js
+define("cordova/plugin/Connection", function(require, exports, module) {
+/**
+ * Network status
+ */
+module.exports = {
+ UNKNOWN: "unknown",
+ ETHERNET: "ethernet",
+ WIFI: "wifi",
+ CELL_2G: "2g",
+ CELL_3G: "3g",
+ CELL_4G: "4g",
+ NONE: "none"
+};
+});
+
+// file: lib/common/plugin/Contact.js
+define("cordova/plugin/Contact", function(require, exports, module) {
+var exec = require('cordova/exec'),
+ ContactError = require('cordova/plugin/ContactError'),
+ utils = require('cordova/utils');
+
+/**
+* Converts primitives into Complex Object
+* Currently only used for Date fields
+*/
+function convertIn(contact) {
+ var value = contact.birthday;
+ try {
+ contact.birthday = new Date(parseFloat(value));
+ } catch (exception){
+ console.log("Cordova Contact convertIn error: exception creating date.");
+ }
+ return contact;
+}
+
+/**
+* Converts Complex objects into primitives
+* Only conversion at present is for Dates.
+**/
+
+function convertOut(contact) {
+ var value = contact.birthday;
+ if (value !== null) {
+ // try to make it a Date object if it is not already
+ if (!utils.isDate(value)){
+ try {
+ value = new Date(value);
+ } catch(exception){
+ value = null;
+ }
+ }
+ if (utils.isDate(value)){
+ value = value.valueOf(); // convert to milliseconds
+ }
+ contact.birthday = value;
+ }
+ return contact;
+}
+
+/**
+* Contains information about a single contact.
+* @constructor
+* @param {DOMString} id unique identifier
+* @param {DOMString} displayName
+* @param {ContactName} name
+* @param {DOMString} nickname
+* @param {Array.} phoneNumbers array of phone numbers
+* @param {Array.} emails array of email addresses
+* @param {Array.} addresses array of addresses
+* @param {Array.} ims instant messaging user ids
+* @param {Array.} organizations
+* @param {DOMString} birthday contact's birthday
+* @param {DOMString} note user notes about contact
+* @param {Array.} photos
+* @param {Array.} categories
+* @param {Array.} urls contact's web sites
+*/
+var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses,
+ ims, organizations, birthday, note, photos, categories, urls) {
+ this.id = id || null;
+ this.rawId = null;
+ this.displayName = displayName || null;
+ this.name = name || null; // ContactName
+ this.nickname = nickname || null;
+ this.phoneNumbers = phoneNumbers || null; // ContactField[]
+ this.emails = emails || null; // ContactField[]
+ this.addresses = addresses || null; // ContactAddress[]
+ this.ims = ims || null; // ContactField[]
+ this.organizations = organizations || null; // ContactOrganization[]
+ this.birthday = birthday || null;
+ this.note = note || null;
+ this.photos = photos || null; // ContactField[]
+ this.categories = categories || null; // ContactField[]
+ this.urls = urls || null; // ContactField[]
+};
+
+/**
+* Removes contact from device storage.
+* @param successCB success callback
+* @param errorCB error callback
+*/
+Contact.prototype.remove = function(successCB, errorCB) {
+ var fail = function(code) {
+ errorCB(new ContactError(code));
+ };
+ if (this.id === null) {
+ fail(ContactError.UNKNOWN_ERROR);
+ }
+ else {
+ exec(successCB, fail, "Contacts", "remove", [this.id]);
+ }
+};
+
+/**
+* Creates a deep copy of this Contact.
+* With the contact ID set to null.
+* @return copy of this Contact
+*/
+Contact.prototype.clone = function() {
+ var clonedContact = utils.clone(this);
+ var i;
+ clonedContact.id = null;
+ clonedContact.rawId = null;
+ // Loop through and clear out any id's in phones, emails, etc.
+ if (clonedContact.phoneNumbers) {
+ for (i = 0; i < clonedContact.phoneNumbers.length; i++) {
+ clonedContact.phoneNumbers[i].id = null;
+ }
+ }
+ if (clonedContact.emails) {
+ for (i = 0; i < clonedContact.emails.length; i++) {
+ clonedContact.emails[i].id = null;
+ }
+ }
+ if (clonedContact.addresses) {
+ for (i = 0; i < clonedContact.addresses.length; i++) {
+ clonedContact.addresses[i].id = null;
+ }
+ }
+ if (clonedContact.ims) {
+ for (i = 0; i < clonedContact.ims.length; i++) {
+ clonedContact.ims[i].id = null;
+ }
+ }
+ if (clonedContact.organizations) {
+ for (i = 0; i < clonedContact.organizations.length; i++) {
+ clonedContact.organizations[i].id = null;
+ }
+ }
+ if (clonedContact.categories) {
+ for (i = 0; i < clonedContact.categories.length; i++) {
+ clonedContact.categories[i].id = null;
+ }
+ }
+ if (clonedContact.photos) {
+ for (i = 0; i < clonedContact.photos.length; i++) {
+ clonedContact.photos[i].id = null;
+ }
+ }
+ if (clonedContact.urls) {
+ for (i = 0; i < clonedContact.urls.length; i++) {
+ clonedContact.urls[i].id = null;
+ }
+ }
+ return clonedContact;
+};
+
+/**
+* Persists contact to device storage.
+* @param successCB success callback
+* @param errorCB error callback
+*/
+Contact.prototype.save = function(successCB, errorCB) {
+ var fail = function(code) {
+ errorCB(new ContactError(code));
+ };
+ var success = function(result) {
+ if (result) {
+ if (typeof successCB === 'function') {
+ var fullContact = require('cordova/plugin/contacts').create(result);
+ successCB(convertIn(fullContact));
+ }
+ }
+ else {
+ // no Entry object returned
+ fail(ContactError.UNKNOWN_ERROR);
+ }
+ };
+ var dupContact = convertOut(utils.clone(this));
+ exec(success, fail, "Contacts", "save", [dupContact]);
+};
+
+
+module.exports = Contact;
+
+});
+
+// file: lib/common/plugin/ContactAddress.js
+define("cordova/plugin/ContactAddress", function(require, exports, module) {
+/**
+* Contact address.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code
+* @param formatted // NOTE: not a W3C standard
+* @param streetAddress
+* @param locality
+* @param region
+* @param postalCode
+* @param country
+*/
+
+var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) {
+ this.id = null;
+ this.pref = (typeof pref != 'undefined' ? pref : false);
+ this.type = type || null;
+ this.formatted = formatted || null;
+ this.streetAddress = streetAddress || null;
+ this.locality = locality || null;
+ this.region = region || null;
+ this.postalCode = postalCode || null;
+ this.country = country || null;
+};
+
+module.exports = ContactAddress;
+});
+
+// file: lib/common/plugin/ContactError.js
+define("cordova/plugin/ContactError", function(require, exports, module) {
+/**
+ * ContactError.
+ * An error code assigned by an implementation when an error has occured
+ * @constructor
+ */
+var ContactError = function(err) {
+ this.code = (typeof err != 'undefined' ? err : null);
+};
+
+/**
+ * Error codes
+ */
+ContactError.UNKNOWN_ERROR = 0;
+ContactError.INVALID_ARGUMENT_ERROR = 1;
+ContactError.TIMEOUT_ERROR = 2;
+ContactError.PENDING_OPERATION_ERROR = 3;
+ContactError.IO_ERROR = 4;
+ContactError.NOT_SUPPORTED_ERROR = 5;
+ContactError.PERMISSION_DENIED_ERROR = 20;
+
+module.exports = ContactError;
+});
+
+// file: lib/common/plugin/ContactField.js
+define("cordova/plugin/ContactField", function(require, exports, module) {
+/**
+* Generic contact field.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard
+* @param type
+* @param value
+* @param pref
+*/
+var ContactField = function(type, value, pref) {
+ this.id = null;
+ this.type = (type && type.toString()) || null;
+ this.value = (value && value.toString()) || null;
+ this.pref = (typeof pref != 'undefined' ? pref : false);
+};
+
+module.exports = ContactField;
+});
+
+// file: lib/common/plugin/ContactFindOptions.js
+define("cordova/plugin/ContactFindOptions", function(require, exports, module) {
+/**
+ * ContactFindOptions.
+ * @constructor
+ * @param filter used to match contacts against
+ * @param multiple boolean used to determine if more than one contact should be returned
+ */
+
+var ContactFindOptions = function(filter, multiple) {
+ this.filter = filter || '';
+ this.multiple = (typeof multiple != 'undefined' ? multiple : false);
+};
+
+module.exports = ContactFindOptions;
+});
+
+// file: lib/common/plugin/ContactName.js
+define("cordova/plugin/ContactName", function(require, exports, module) {
+/**
+* Contact name.
+* @constructor
+* @param formatted // NOTE: not part of W3C standard
+* @param familyName
+* @param givenName
+* @param middle
+* @param prefix
+* @param suffix
+*/
+var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) {
+ this.formatted = formatted || null;
+ this.familyName = familyName || null;
+ this.givenName = givenName || null;
+ this.middleName = middle || null;
+ this.honorificPrefix = prefix || null;
+ this.honorificSuffix = suffix || null;
+};
+
+module.exports = ContactName;
+});
+
+// file: lib/common/plugin/ContactOrganization.js
+define("cordova/plugin/ContactOrganization", function(require, exports, module) {
+/**
+* Contact organization.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard
+* @param name
+* @param dept
+* @param title
+* @param startDate
+* @param endDate
+* @param location
+* @param desc
+*/
+
+var ContactOrganization = function(pref, type, name, dept, title) {
+ this.id = null;
+ this.pref = (typeof pref != 'undefined' ? pref : false);
+ this.type = type || null;
+ this.name = name || null;
+ this.department = dept || null;
+ this.title = title || null;
+};
+
+module.exports = ContactOrganization;
+});
+
+// file: lib/common/plugin/Coordinates.js
+define("cordova/plugin/Coordinates", function(require, exports, module) {
+/**
+ * This class contains position information.
+ * @param {Object} lat
+ * @param {Object} lng
+ * @param {Object} alt
+ * @param {Object} acc
+ * @param {Object} head
+ * @param {Object} vel
+ * @param {Object} altacc
+ * @constructor
+ */
+var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) {
+ /**
+ * The latitude of the position.
+ */
+ this.latitude = lat;
+ /**
+ * The longitude of the position,
+ */
+ this.longitude = lng;
+ /**
+ * The accuracy of the position.
+ */
+ this.accuracy = acc;
+ /**
+ * The altitude of the position.
+ */
+ this.altitude = (alt !== undefined ? alt : null);
+ /**
+ * The direction the device is moving at the position.
+ */
+ this.heading = (head !== undefined ? head : null);
+ /**
+ * The velocity with which the device is moving at the position.
+ */
+ this.speed = (vel !== undefined ? vel : null);
+
+ if (this.speed === 0 || this.speed === null) {
+ this.heading = NaN;
+ }
+
+ /**
+ * The altitude accuracy of the position.
+ */
+ this.altitudeAccuracy = (altacc !== undefined) ? altacc : null;
+};
+
+module.exports = Coordinates;
+
+});
+
+// file: lib/common/plugin/DirectoryEntry.js
+define("cordova/plugin/DirectoryEntry", function(require, exports, module) {
+var utils = require('cordova/utils'),
+ exec = require('cordova/exec'),
+ Entry = require('cordova/plugin/Entry'),
+ FileError = require('cordova/plugin/FileError'),
+ DirectoryReader = require('cordova/plugin/DirectoryReader');
+
+/**
+ * An interface representing a directory on the file system.
+ *
+ * {boolean} isFile always false (readonly)
+ * {boolean} isDirectory always true (readonly)
+ * {DOMString} name of the directory, excluding the path leading to it (readonly)
+ * {DOMString} fullPath the absolute full path to the directory (readonly)
+ * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly)
+ */
+var DirectoryEntry = function(name, fullPath) {
+ DirectoryEntry.__super__.constructor.apply(this, [false, true, name, fullPath]);
+};
+
+utils.extend(DirectoryEntry, Entry);
+
+/**
+ * Creates a new DirectoryReader to read entries from this directory
+ */
+DirectoryEntry.prototype.createReader = function() {
+ return new DirectoryReader(this.fullPath);
+};
+
+/**
+ * Creates or looks up a directory
+ *
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory
+ * @param {Flags} options to create or excluively create the directory
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) {
+ var win = typeof successCallback !== 'function' ? null : function(result) {
+ var entry = new DirectoryEntry(result.name, result.fullPath);
+ successCallback(entry);
+ };
+ var fail = typeof errorCallback !== 'function' ? null : function(code) {
+ errorCallback(new FileError(code));
+ };
+ exec(win, fail, "File", "getDirectory", [this.fullPath, path, options]);
+};
+
+/**
+ * Deletes a directory and all of it's contents
+ *
+ * @param {Function} successCallback is called with no parameters
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) {
+ var fail = typeof errorCallback !== 'function' ? null : function(code) {
+ errorCallback(new FileError(code));
+ };
+ exec(successCallback, fail, "File", "removeRecursively", [this.fullPath]);
+};
+
+/**
+ * Creates or looks up a file
+ *
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file
+ * @param {Flags} options to create or excluively create the file
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) {
+ var win = typeof successCallback !== 'function' ? null : function(result) {
+ var FileEntry = require('cordova/plugin/FileEntry');
+ var entry = new FileEntry(result.name, result.fullPath);
+ successCallback(entry);
+ };
+ var fail = typeof errorCallback !== 'function' ? null : function(code) {
+ errorCallback(new FileError(code));
+ };
+ exec(win, fail, "File", "getFile", [this.fullPath, path, options]);
+};
+
+module.exports = DirectoryEntry;
+
+});
+
+// file: lib/common/plugin/DirectoryReader.js
+define("cordova/plugin/DirectoryReader", function(require, exports, module) {
+var exec = require('cordova/exec'),
+ FileError = require('cordova/plugin/FileError') ;
+
+/**
+ * An interface that lists the files and directories in a directory.
+ */
+function DirectoryReader(path) {
+ this.path = path || null;
+}
+
+/**
+ * Returns a list of entries from a directory.
+ *
+ * @param {Function} successCallback is called with a list of entries
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) {
+ var win = typeof successCallback !== 'function' ? null : function(result) {
+ var retVal = [];
+ for (var i=0; i][;base64],
+ *
+ * @param file {File} File object containing file properties
+ */
+FileReader.prototype.readAsDataURL = function(file) {
+ this.fileName = "";
+ if (typeof file.fullPath === "undefined") {
+ this.fileName = file;
+ } else {
+ this.fileName = file.fullPath;
+ }
+
+ // Already loading something
+ if (this.readyState == FileReader.LOADING) {
+ throw new FileError(FileError.INVALID_STATE_ERR);
+ }
+
+ // LOADING state
+ this.readyState = FileReader.LOADING;
+
+ // If loadstart callback
+ if (typeof this.onloadstart === "function") {
+ this.onloadstart(new ProgressEvent("loadstart", {target:this}));
+ }
+
+ var me = this;
+
+ // Read file
+ exec(
+ // Success callback
+ function(r) {
+ // If DONE (cancelled), then don't do anything
+ if (me.readyState === FileReader.DONE) {
+ return;
+ }
+
+ // DONE state
+ me.readyState = FileReader.DONE;
+
+ // Save result
+ me.result = r;
+
+ // If onload callback
+ if (typeof me.onload === "function") {
+ me.onload(new ProgressEvent("load", {target:me}));
+ }
+
+ // If onloadend callback
+ if (typeof me.onloadend === "function") {
+ me.onloadend(new ProgressEvent("loadend", {target:me}));
+ }
+ },
+ // Error callback
+ function(e) {
+ // If DONE (cancelled), then don't do anything
+ if (me.readyState === FileReader.DONE) {
+ return;
+ }
+
+ // DONE state
+ me.readyState = FileReader.DONE;
+
+ me.result = null;
+
+ // Save error
+ me.error = new FileError(e);
+
+ // If onerror callback
+ if (typeof me.onerror === "function") {
+ me.onerror(new ProgressEvent("error", {target:me}));
+ }
+
+ // If onloadend callback
+ if (typeof me.onloadend === "function") {
+ me.onloadend(new ProgressEvent("loadend", {target:me}));
+ }
+ }, "File", "readAsDataURL", [this.fileName]);
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file {File} File object containing file properties
+ */
+FileReader.prototype.readAsBinaryString = function(file) {
+ // TODO - Can't return binary data to browser.
+ console.log('method "readAsBinaryString" is not supported at this time.');
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file {File} File object containing file properties
+ */
+FileReader.prototype.readAsArrayBuffer = function(file) {
+ // TODO - Can't return binary data to browser.
+ console.log('This method is not supported at this time.');
+};
+
+module.exports = FileReader;
+});
+
+// file: lib/common/plugin/FileSystem.js
+define("cordova/plugin/FileSystem", function(require, exports, module) {
+var DirectoryEntry = require('cordova/plugin/DirectoryEntry');
+
+/**
+ * An interface representing a file system
+ *
+ * @constructor
+ * {DOMString} name the unique name of the file system (readonly)
+ * {DirectoryEntry} root directory of the file system (readonly)
+ */
+var FileSystem = function(name, root) {
+ this.name = name || null;
+ if (root) {
+ this.root = new DirectoryEntry(root.name, root.fullPath);
+ }
+};
+
+module.exports = FileSystem;
+
+});
+
+// file: lib/common/plugin/FileTransfer.js
+define("cordova/plugin/FileTransfer", function(require, exports, module) {
+var exec = require('cordova/exec'),
+ FileTransferError = require('cordova/plugin/FileTransferError');
+
+/**
+ * FileTransfer uploads a file to a remote server.
+ * @constructor
+ */
+var FileTransfer = function() {};
+
+/**
+* Given an absolute file path, uploads a file on the device to a remote server
+* using a multipart HTTP request.
+* @param filePath {String} Full path of the file on the device
+* @param server {String} URL of the server to receive the file
+* @param successCallback (Function} Callback to be invoked when upload has completed
+* @param errorCallback {Function} Callback to be invoked upon error
+* @param options {FileUploadOptions} Optional parameters such as file name and mimetype
+* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
+*/
+FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) {
+ // sanity parameter checking
+ if (!filePath || !server) throw new Error("FileTransfer.upload requires filePath and server URL parameters at the minimum.");
+ // check for options
+ var fileKey = null;
+ var fileName = null;
+ var mimeType = null;
+ var params = null;
+ var chunkedMode = true;
+ if (options) {
+ fileKey = options.fileKey;
+ fileName = options.fileName;
+ mimeType = options.mimeType;
+ if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") {
+ chunkedMode = options.chunkedMode;
+ }
+ if (options.params) {
+ params = options.params;
+ }
+ else {
+ params = {};
+ }
+ }
+
+ var fail = function(e) {
+ var error = new FileTransferError(e.code, e.source, e.target, e.http_status);
+ errorCallback(error);
+ };
+
+ exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode]);
+};
+
+/**
+ * Downloads a file form a given URL and saves it to the specified directory.
+ * @param source {String} URL of the server to receive the file
+ * @param target {String} Full path of the file on the device
+ * @param successCallback (Function} Callback to be invoked when upload has completed
+ * @param errorCallback {Function} Callback to be invoked upon error
+ */
+FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) {
+ // sanity parameter checking
+ if (!source || !target) throw new Error("FileTransfer.download requires source URI and target URI parameters at the minimum.");
+ var win = function(result) {
+ var entry = null;
+ if (result.isDirectory) {
+ entry = new (require('cordova/plugin/DirectoryEntry'))();
+ }
+ else if (result.isFile) {
+ entry = new (require('cordova/plugin/FileEntry'))();
+ }
+ entry.isDirectory = result.isDirectory;
+ entry.isFile = result.isFile;
+ entry.name = result.name;
+ entry.fullPath = result.fullPath;
+ successCallback(entry);
+ };
+
+ var fail = function(e) {
+ var error = new FileTransferError(e.code, e.source, e.target, e.http_status);
+ errorCallback(error);
+ };
+
+ exec(win, errorCallback, 'FileTransfer', 'download', [source, target]);
+};
+
+module.exports = FileTransfer;
+
+});
+
+// file: lib/common/plugin/FileTransferError.js
+define("cordova/plugin/FileTransferError", function(require, exports, module) {
+/**
+ * FileTransferError
+ * @constructor
+ */
+var FileTransferError = function(code, source, target, status) {
+ this.code = code || null;
+ this.source = source || null;
+ this.target = target || null;
+ this.http_status = status || null;
+};
+
+FileTransferError.FILE_NOT_FOUND_ERR = 1;
+FileTransferError.INVALID_URL_ERR = 2;
+FileTransferError.CONNECTION_ERR = 3;
+
+module.exports = FileTransferError;
+
+});
+
+// file: lib/common/plugin/FileUploadOptions.js
+define("cordova/plugin/FileUploadOptions", function(require, exports, module) {
+/**
+ * Options to customize the HTTP request used to upload files.
+ * @constructor
+ * @param fileKey {String} Name of file request parameter.
+ * @param fileName {String} Filename to be used by the server. Defaults to image.jpg.
+ * @param mimeType {String} Mimetype of the uploaded file. Defaults to image/jpeg.
+ * @param params {Object} Object with key: value params to send to the server.
+ */
+var FileUploadOptions = function(fileKey, fileName, mimeType, params) {
+ this.fileKey = fileKey || null;
+ this.fileName = fileName || null;
+ this.mimeType = mimeType || null;
+ this.params = params || null;
+};
+
+module.exports = FileUploadOptions;
+});
+
+// file: lib/common/plugin/FileUploadResult.js
+define("cordova/plugin/FileUploadResult", function(require, exports, module) {
+/**
+ * FileUploadResult
+ * @constructor
+ */
+var FileUploadResult = function() {
+ this.bytesSent = 0;
+ this.responseCode = null;
+ this.response = null;
+};
+
+module.exports = FileUploadResult;
+});
+
+// file: lib/common/plugin/FileWriter.js
+define("cordova/plugin/FileWriter", function(require, exports, module) {
+var exec = require('cordova/exec'),
+ FileError = require('cordova/plugin/FileError'),
+ ProgressEvent = require('cordova/plugin/ProgressEvent');
+
+/**
+ * This class writes to the mobile device file system.
+ *
+ * For Android:
+ * The root directory is the root of the file system.
+ * To write to the SD card, the file name is "sdcard/my_file.txt"
+ *
+ * @constructor
+ * @param file {File} File object containing file properties
+ * @param append if true write to the end of the file, otherwise overwrite the file
+ */
+var FileWriter = function(file) {
+ this.fileName = "";
+ this.length = 0;
+ if (file) {
+ this.fileName = file.fullPath || file;
+ this.length = file.size || 0;
+ }
+ // default is to write at the beginning of the file
+ this.position = 0;
+
+ this.readyState = 0; // EMPTY
+
+ this.result = null;
+
+ // Error
+ this.error = null;
+
+ // Event handlers
+ this.onwritestart = null; // When writing starts
+ this.onprogress = null; // While writing the file, and reporting partial file data
+ this.onwrite = null; // When the write has successfully completed.
+ this.onwriteend = null; // When the request has completed (either in success or failure).
+ this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method.
+ this.onerror = null; // When the write has failed (see errors).
+};
+
+// States
+FileWriter.INIT = 0;
+FileWriter.WRITING = 1;
+FileWriter.DONE = 2;
+
+/**
+ * Abort writing file.
+ */
+FileWriter.prototype.abort = function() {
+ // check for invalid state
+ if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) {
+ throw new FileError(FileError.INVALID_STATE_ERR);
+ }
+
+ // set error
+ this.error = new FileError(FileError.ABORT_ERR);
+
+ this.readyState = FileWriter.DONE;
+
+ // If abort callback
+ if (typeof this.onabort === "function") {
+ this.onabort(new ProgressEvent("abort", {"target":this}));
+ }
+
+ // If write end callback
+ if (typeof this.onwriteend === "function") {
+ this.onwriteend(new ProgressEvent("writeend", {"target":this}));
+ }
+};
+
+/**
+ * Writes data to the file
+ *
+ * @param text to be written
+ */
+FileWriter.prototype.write = function(text) {
+ // Throw an exception if we are already writing a file
+ if (this.readyState === FileWriter.WRITING) {
+ throw new FileError(FileError.INVALID_STATE_ERR);
+ }
+
+ // WRITING state
+ this.readyState = FileWriter.WRITING;
+
+ var me = this;
+
+ // If onwritestart callback
+ if (typeof me.onwritestart === "function") {
+ me.onwritestart(new ProgressEvent("writestart", {"target":me}));
+ }
+
+ // Write file
+ exec(
+ // Success callback
+ function(r) {
+ // If DONE (cancelled), then don't do anything
+ if (me.readyState === FileWriter.DONE) {
+ return;
+ }
+
+ // position always increases by bytes written because file would be extended
+ me.position += r;
+ // The length of the file is now where we are done writing.
+
+ me.length = me.position;
+
+ // DONE state
+ me.readyState = FileWriter.DONE;
+
+ // If onwrite callback
+ if (typeof me.onwrite === "function") {
+ me.onwrite(new ProgressEvent("write", {"target":me}));
+ }
+
+ // If onwriteend callback
+ if (typeof me.onwriteend === "function") {
+ me.onwriteend(new ProgressEvent("writeend", {"target":me}));
+ }
+ },
+ // Error callback
+ function(e) {
+ // If DONE (cancelled), then don't do anything
+ if (me.readyState === FileWriter.DONE) {
+ return;
+ }
+
+ // DONE state
+ me.readyState = FileWriter.DONE;
+
+ // Save error
+ me.error = new FileError(e);
+
+ // If onerror callback
+ if (typeof me.onerror === "function") {
+ me.onerror(new ProgressEvent("error", {"target":me}));
+ }
+
+ // If onwriteend callback
+ if (typeof me.onwriteend === "function") {
+ me.onwriteend(new ProgressEvent("writeend", {"target":me}));
+ }
+ }, "File", "write", [this.fileName, text, this.position]);
+};
+
+/**
+ * Moves the file pointer to the location specified.
+ *
+ * If the offset is a negative number the position of the file
+ * pointer is rewound. If the offset is greater than the file
+ * size the position is set to the end of the file.
+ *
+ * @param offset is the location to move the file pointer to.
+ */
+FileWriter.prototype.seek = function(offset) {
+ // Throw an exception if we are already writing a file
+ if (this.readyState === FileWriter.WRITING) {
+ throw new FileError(FileError.INVALID_STATE_ERR);
+ }
+
+ if (!offset && offset !== 0) {
+ return;
+ }
+
+ // See back from end of file.
+ if (offset < 0) {
+ this.position = Math.max(offset + this.length, 0);
+ }
+ // Offset is bigger then file size so set position
+ // to the end of the file.
+ else if (offset > this.length) {
+ this.position = this.length;
+ }
+ // Offset is between 0 and file size so set the position
+ // to start writing.
+ else {
+ this.position = offset;
+ }
+};
+
+/**
+ * Truncates the file to the size specified.
+ *
+ * @param size to chop the file at.
+ */
+FileWriter.prototype.truncate = function(size) {
+ // Throw an exception if we are already writing a file
+ if (this.readyState === FileWriter.WRITING) {
+ throw new FileError(FileError.INVALID_STATE_ERR);
+ }
+
+ // WRITING state
+ this.readyState = FileWriter.WRITING;
+
+ var me = this;
+
+ // If onwritestart callback
+ if (typeof me.onwritestart === "function") {
+ me.onwritestart(new ProgressEvent("writestart", {"target":this}));
+ }
+
+ // Write file
+ exec(
+ // Success callback
+ function(r) {
+ // If DONE (cancelled), then don't do anything
+ if (me.readyState === FileWriter.DONE) {
+ return;
+ }
+
+ // DONE state
+ me.readyState = FileWriter.DONE;
+
+ // Update the length of the file
+ me.length = r;
+ me.position = Math.min(me.position, r);
+
+ // If onwrite callback
+ if (typeof me.onwrite === "function") {
+ me.onwrite(new ProgressEvent("write", {"target":me}));
+ }
+
+ // If onwriteend callback
+ if (typeof me.onwriteend === "function") {
+ me.onwriteend(new ProgressEvent("writeend", {"target":me}));
+ }
+ },
+ // Error callback
+ function(e) {
+ // If DONE (cancelled), then don't do anything
+ if (me.readyState === FileWriter.DONE) {
+ return;
+ }
+
+ // DONE state
+ me.readyState = FileWriter.DONE;
+
+ // Save error
+ me.error = new FileError(e);
+
+ // If onerror callback
+ if (typeof me.onerror === "function") {
+ me.onerror(new ProgressEvent("error", {"target":me}));
+ }
+
+ // If onwriteend callback
+ if (typeof me.onwriteend === "function") {
+ me.onwriteend(new ProgressEvent("writeend", {"target":me}));
+ }
+ }, "File", "truncate", [this.fileName, size]);
+};
+
+module.exports = FileWriter;
+
+});
+
+// file: lib/common/plugin/Flags.js
+define("cordova/plugin/Flags", function(require, exports, module) {
+/**
+ * Supplies arguments to methods that lookup or create files and directories.
+ *
+ * @param create
+ * {boolean} file or directory if it doesn't exist
+ * @param exclusive
+ * {boolean} used with create; if true the command will fail if
+ * target path exists
+ */
+function Flags(create, exclusive) {
+ this.create = create || false;
+ this.exclusive = exclusive || false;
+}
+
+module.exports = Flags;
+});
+
+// file: lib/common/plugin/LocalFileSystem.js
+define("cordova/plugin/LocalFileSystem", function(require, exports, module) {
+var exec = require('cordova/exec');
+
+/**
+ * Represents a local file system.
+ */
+var LocalFileSystem = function() {
+
+};
+
+LocalFileSystem.TEMPORARY = 0; //temporary, with no guarantee of persistence
+LocalFileSystem.PERSISTENT = 1; //persistent
+
+module.exports = LocalFileSystem;
+});
+
+// file: lib/common/plugin/Media.js
+define("cordova/plugin/Media", function(require, exports, module) {
+var utils = require('cordova/utils'),
+ exec = require('cordova/exec');
+
+var mediaObjects = {};
+
+/**
+ * This class provides access to the device media, interfaces to both sound and video
+ *
+ * @constructor
+ * @param src The file name or url to play
+ * @param successCallback The callback to be called when the file is done playing or recording.
+ * successCallback()
+ * @param errorCallback The callback to be called if there is an error.
+ * errorCallback(int errorCode) - OPTIONAL
+ * @param statusCallback The callback to be called when media status has changed.
+ * statusCallback(int statusCode) - OPTIONAL
+ */
+var Media = function(src, successCallback, errorCallback, statusCallback) {
+
+ // successCallback optional
+ if (successCallback && (typeof successCallback !== "function")) {
+ console.log("Media Error: successCallback is not a function");
+ return;
+ }
+
+ // errorCallback optional
+ if (errorCallback && (typeof errorCallback !== "function")) {
+ console.log("Media Error: errorCallback is not a function");
+ return;
+ }
+
+ // statusCallback optional
+ if (statusCallback && (typeof statusCallback !== "function")) {
+ console.log("Media Error: statusCallback is not a function");
+ return;
+ }
+
+ this.id = utils.createUUID();
+ mediaObjects[this.id] = this;
+ this.src = src;
+ this.successCallback = successCallback;
+ this.errorCallback = errorCallback;
+ this.statusCallback = statusCallback;
+ this._duration = -1;
+ this._position = -1;
+ exec(null, this.errorCallback, "Media", "create", [this.id, this.src]);
+};
+
+// Media messages
+Media.MEDIA_STATE = 1;
+Media.MEDIA_DURATION = 2;
+Media.MEDIA_POSITION = 3;
+Media.MEDIA_ERROR = 9;
+
+// Media states
+Media.MEDIA_NONE = 0;
+Media.MEDIA_STARTING = 1;
+Media.MEDIA_RUNNING = 2;
+Media.MEDIA_PAUSED = 3;
+Media.MEDIA_STOPPED = 4;
+Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"];
+
+// "static" function to return existing objs.
+Media.get = function(id) {
+ return mediaObjects[id];
+};
+
+/**
+ * Start or resume playing audio file.
+ */
+Media.prototype.play = function(options) {
+ exec(null, null, "Media", "startPlayingAudio", [this.id, this.src, options]);
+};
+
+/**
+ * Stop playing audio file.
+ */
+Media.prototype.stop = function() {
+ var me = this;
+ exec(function() {
+ me._position = 0;
+ me.successCallback();
+ }, this.errorCallback, "Media", "stopPlayingAudio", [this.id]);
+};
+
+/**
+ * Seek or jump to a new time in the track..
+ */
+Media.prototype.seekTo = function(milliseconds) {
+ var me = this;
+ exec(function(p) {
+ me._position = p;
+ }, this.errorCallback, "Media", "seekToAudio", [this.id, milliseconds]);
+};
+
+/**
+ * Pause playing audio file.
+ */
+Media.prototype.pause = function() {
+ exec(null, this.errorCallback, "Media", "pausePlayingAudio", [this.id]);
+};
+
+/**
+ * Get duration of an audio file.
+ * The duration is only set for audio that is playing, paused or stopped.
+ *
+ * @return duration or -1 if not known.
+ */
+Media.prototype.getDuration = function() {
+ return this._duration;
+};
+
+/**
+ * Get position of audio.
+ */
+Media.prototype.getCurrentPosition = function(success, fail) {
+ var me = this;
+ exec(function(p) {
+ me._position = p;
+ success(p);
+ }, fail, "Media", "getCurrentPositionAudio", [this.id]);
+};
+
+/**
+ * Start recording audio file.
+ */
+Media.prototype.startRecord = function() {
+ exec(this.successCallback, this.errorCallback, "Media", "startRecordingAudio", [this.id, this.src]);
+};
+
+/**
+ * Stop recording audio file.
+ */
+Media.prototype.stopRecord = function() {
+ exec(this.successCallback, this.errorCallback, "Media", "stopRecordingAudio", [this.id]);
+};
+
+/**
+ * Release the resources.
+ */
+Media.prototype.release = function() {
+ exec(null, this.errorCallback, "Media", "release", [this.id]);
+};
+
+/**
+ * Adjust the volume.
+ */
+Media.prototype.setVolume = function(volume) {
+ exec(null, null, "Media", "setVolume", [this.id, volume]);
+};
+
+/**
+ * Audio has status update.
+ * PRIVATE
+ *
+ * @param id The media object id (string)
+ * @param status The status code (int)
+ * @param msg The status message (string)
+ */
+Media.onStatus = function(id, msg, value) {
+ var media = mediaObjects[id];
+ // If state update
+ if (msg === Media.MEDIA_STATE) {
+ if (value === Media.MEDIA_STOPPED) {
+ if (media.successCallback) {
+ media.successCallback();
+ }
+ }
+ if (media.statusCallback) {
+ media.statusCallback(value);
+ }
+ }
+ else if (msg === Media.MEDIA_DURATION) {
+ media._duration = value;
+ }
+ else if (msg === Media.MEDIA_ERROR) {
+ if (media.errorCallback) {
+ // value should be a MediaError object when msg == MEDIA_ERROR
+ media.errorCallback(value);
+ }
+ }
+ else if (msg === Media.MEDIA_POSITION) {
+ media._position = value;
+ }
+};
+
+module.exports = Media;
+});
+
+// file: lib/common/plugin/MediaError.js
+define("cordova/plugin/MediaError", function(require, exports, module) {
+/**
+ * This class contains information about any Media errors.
+ * @constructor
+ */
+var MediaError = function(code, msg) {
+ this.code = (code !== undefined ? code : null);
+ this.message = msg || "";
+};
+
+MediaError.MEDIA_ERR_NONE_ACTIVE = 0;
+MediaError.MEDIA_ERR_ABORTED = 1;
+MediaError.MEDIA_ERR_NETWORK = 2;
+MediaError.MEDIA_ERR_DECODE = 3;
+MediaError.MEDIA_ERR_NONE_SUPPORTED = 4;
+
+module.exports = MediaError;
+});
+
+// file: lib/common/plugin/MediaFile.js
+define("cordova/plugin/MediaFile", function(require, exports, module) {
+var utils = require('cordova/utils'),
+ exec = require('cordova/exec'),
+ File = require('cordova/plugin/File'),
+ CaptureError = require('cordova/plugin/CaptureError');
+/**
+ * Represents a single file.
+ *
+ * name {DOMString} name of the file, without path information
+ * fullPath {DOMString} the full path of the file, including the name
+ * type {DOMString} mime type
+ * lastModifiedDate {Date} last modified date
+ * size {Number} size of the file in bytes
+ */
+var MediaFile = function(name, fullPath, type, lastModifiedDate, size){
+ MediaFile.__super__.constructor.apply(this, arguments);
+};
+
+utils.extend(MediaFile, File);
+
+/**
+ * Request capture format data for a specific file and type
+ *
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ */
+MediaFile.prototype.getFormatData = function(successCallback, errorCallback) {
+ if (typeof this.fullPath === "undefined" || this.fullPath === null) {
+ errorCallback(new CaptureError(CaptureError.CAPTURE_INVALID_ARGUMENT));
+ } else {
+ exec(successCallback, errorCallback, "Capture", "getFormatData", [this.fullPath, this.type]);
+ }
+};
+
+// TODO: can we axe this?
+/**
+ * Casts a PluginResult message property (array of objects) to an array of MediaFile objects
+ * (used in Objective-C and Android)
+ *
+ * @param {PluginResult} pluginResult
+ */
+MediaFile.cast = function(pluginResult) {
+ var mediaFiles = [];
+ for (var i=0; i.dispatchEvent
+ // need to first figure out how to implement EventTarget
+ }
+ }
+ return event;
+ };
+ try {
+ var ev = createEvent({type:"abort",target:document});
+ return function ProgressEvent(type, data) {
+ data.type = type;
+ return createEvent(data);
+ };
+ } catch(e){
+ */
+ return function ProgressEvent(type, dict) {
+ this.type = type;
+ this.bubbles = false;
+ this.cancelBubble = false;
+ this.cancelable = false;
+ this.lengthComputable = false;
+ this.loaded = dict && dict.loaded ? dict.loaded : 0;
+ this.total = dict && dict.total ? dict.total : 0;
+ this.target = dict && dict.target ? dict.target : null;
+ };
+ //}
+})();
+
+module.exports = ProgressEvent;
+});
+
+// file: lib/common/plugin/accelerometer.js
+define("cordova/plugin/accelerometer", function(require, exports, module) {
+/**
+ * This class provides access to device accelerometer data.
+ * @constructor
+ */
+var utils = require("cordova/utils"),
+ exec = require("cordova/exec"),
+ Acceleration = require('cordova/plugin/Acceleration');
+
+// Is the accel sensor running?
+var running = false;
+
+// Keeps reference to watchAcceleration calls.
+var timers = {};
+
+// Array of listeners; used to keep track of when we should call start and stop.
+var listeners = [];
+
+// Last returned acceleration object from native
+var accel = null;
+
+// Tells native to start.
+function start() {
+ exec(function(a) {
+ var tempListeners = listeners.slice(0);
+ accel = new Acceleration(a.x, a.y, a.z, a.timestamp);
+ for (var i = 0, l = tempListeners.length; i < l; i++) {
+ tempListeners[i].win(accel);
+ }
+ }, function(e) {
+ var tempListeners = listeners.slice(0);
+ for (var i = 0, l = tempListeners.length; i < l; i++) {
+ tempListeners[i].fail(e);
+ }
+ }, "Accelerometer", "start", []);
+ running = true;
+}
+
+// Tells native to stop.
+function stop() {
+ exec(null, null, "Accelerometer", "stop", []);
+ running = false;
+}
+
+// Adds a callback pair to the listeners array
+function createCallbackPair(win, fail) {
+ return {win:win, fail:fail};
+}
+
+// Removes a win/fail listener pair from the listeners array
+function removeListeners(l) {
+ var idx = listeners.indexOf(l);
+ if (idx > -1) {
+ listeners.splice(idx, 1);
+ if (listeners.length === 0) {
+ stop();
+ }
+ }
+}
+
+var accelerometer = {
+ /**
+ * Asynchronously aquires the current acceleration.
+ *
+ * @param {Function} successCallback The function to call when the acceleration data is available
+ * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL)
+ * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL)
+ */
+ getCurrentAcceleration: function(successCallback, errorCallback, options) {
+ // successCallback required
+ if (typeof successCallback !== "function") {
+ throw "getCurrentAcceleration must be called with at least a success callback function as first parameter.";
+ }
+
+ var p;
+ var win = function(a) {
+ removeListeners(p);
+ successCallback(a);
+ };
+ var fail = function(e) {
+ removeListeners(p);
+ errorCallback(e);
+ };
+
+ p = createCallbackPair(win, fail);
+ listeners.push(p);
+
+ if (!running) {
+ start();
+ }
+ },
+
+ /**
+ * Asynchronously aquires the acceleration repeatedly at a given interval.
+ *
+ * @param {Function} successCallback The function to call each time the acceleration data is available
+ * @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL)
+ * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL)
+ * @return String The watch id that must be passed to #clearWatch to stop watching.
+ */
+ watchAcceleration: function(successCallback, errorCallback, options) {
+ // Default interval (10 sec)
+ var frequency = (options && options.frequency && typeof options.frequency == 'number') ? options.frequency : 10000;
+
+ // successCallback required
+ if (typeof successCallback !== "function") {
+ throw "watchAcceleration must be called with at least a success callback function as first parameter.";
+ }
+
+ // Keep reference to watch id, and report accel readings as often as defined in frequency
+ var id = utils.createUUID();
+
+ var p = createCallbackPair(function(){}, function(e) {
+ removeListeners(p);
+ errorCallback(e);
+ });
+ listeners.push(p);
+
+ timers[id] = {
+ timer:window.setInterval(function() {
+ if (accel) {
+ successCallback(accel);
+ }
+ }, frequency),
+ listeners:p
+ };
+
+ if (running) {
+ // If we're already running then immediately invoke the success callback
+ // but only if we have retreived a value, sample code does not check for null ...
+ if(accel) {
+ successCallback(accel);
+ }
+ } else {
+ start();
+ }
+
+ return id;
+ },
+
+ /**
+ * Clears the specified accelerometer watch.
+ *
+ * @param {String} id The id of the watch returned from #watchAcceleration.
+ */
+ clearWatch: function(id) {
+ // Stop javascript timer & remove from timer list
+ if (id && timers[id]) {
+ window.clearInterval(timers[id].timer);
+ removeListeners(timers[id].listeners);
+ delete timers[id];
+ }
+ }
+};
+
+module.exports = accelerometer;
+
+});
+
+// file: lib/android/plugin/android/app.js
+define("cordova/plugin/android/app", function(require, exports, module) {
+var exec = require('cordova/exec');
+
+module.exports = {
+ /**
+ * Clear the resource cache.
+ */
+ clearCache:function() {
+ exec(null, null, "App", "clearCache", []);
+ },
+
+ /**
+ * Load the url into the webview or into new browser instance.
+ *
+ * @param url The URL to load
+ * @param props Properties that can be passed in to the activity:
+ * wait: int => wait msec before loading URL
+ * loadingDialog: "Title,Message" => display a native loading dialog
+ * loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error
+ * clearHistory: boolean => clear webview history (default=false)
+ * openExternal: boolean => open in a new browser (default=false)
+ *
+ * Example:
+ * navigator.app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000});
+ */
+ loadUrl:function(url, props) {
+ exec(null, null, "App", "loadUrl", [url, props]);
+ },
+
+ /**
+ * Cancel loadUrl that is waiting to be loaded.
+ */
+ cancelLoadUrl:function() {
+ exec(null, null, "App", "cancelLoadUrl", []);
+ },
+
+ /**
+ * Clear web history in this web view.
+ * Instead of BACK button loading the previous web page, it will exit the app.
+ */
+ clearHistory:function() {
+ exec(null, null, "App", "clearHistory", []);
+ },
+
+ /**
+ * Go to previous page displayed.
+ * This is the same as pressing the backbutton on Android device.
+ */
+ backHistory:function() {
+ exec(null, null, "App", "backHistory", []);
+ },
+
+ /**
+ * Override the default behavior of the Android back button.
+ * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired.
+ *
+ * Note: The user should not have to call this method. Instead, when the user
+ * registers for the "backbutton" event, this is automatically done.
+ *
+ * @param override T=override, F=cancel override
+ */
+ overrideBackbutton:function(override) {
+ exec(null, null, "App", "overrideBackbutton", [override]);
+ },
+
+ /**
+ * Exit and terminate the application.
+ */
+ exitApp:function() {
+ return exec(null, null, "App", "exitApp", []);
+ }
+};
+});
+
+// file: lib/android/plugin/android/callback.js
+define("cordova/plugin/android/callback", function(require, exports, module) {
+var port = null,
+ token = null,
+ cordova = require('cordova'),
+ polling = require('cordova/plugin/android/polling'),
+ callback = function() {
+ // Exit if shutting down app
+ if (cordova.shuttingDown) {
+ return;
+ }
+
+ // If polling flag was changed, start using polling from now on
+ if (cordova.UsePolling) {
+ polling();
+ return;
+ }
+
+ var xmlhttp = new XMLHttpRequest();
+
+ // Callback function when XMLHttpRequest is ready
+ xmlhttp.onreadystatechange=function(){
+ if(xmlhttp.readyState === 4){
+
+ // Exit if shutting down app
+ if (cordova.shuttingDown) {
+ return;
+ }
+
+ // If callback has JavaScript statement to execute
+ if (xmlhttp.status === 200) {
+
+ // Need to url decode the response
+ var msg = decodeURIComponent(xmlhttp.responseText);
+ setTimeout(function() {
+ try {
+ var t = eval(msg);
+ }
+ catch (e) {
+ // If we're getting an error here, seeing the message will help in debugging
+ console.log("JSCallback: Message from Server: " + msg);
+ console.log("JSCallback Error: "+e);
+ }
+ }, 1);
+ setTimeout(callback, 1);
+ }
+
+ // If callback ping (used to keep XHR request from timing out)
+ else if (xmlhttp.status === 404) {
+ setTimeout(callback, 10);
+ }
+
+ // If security error
+ else if (xmlhttp.status === 403) {
+ console.log("JSCallback Error: Invalid token. Stopping callbacks.");
+ }
+
+ // If server is stopping
+ else if (xmlhttp.status === 503) {
+ console.log("JSCallback Server Closed: Stopping callbacks.");
+ }
+
+ // If request wasn't GET
+ else if (xmlhttp.status === 400) {
+ console.log("JSCallback Error: Bad request. Stopping callbacks.");
+ }
+
+ // If error, revert to polling
+ else {
+ console.log("JSCallback Error: Request failed.");
+ cordova.UsePolling = true;
+ polling();
+ }
+ }
+ };
+
+ if (port === null) {
+ port = prompt("getPort", "gap_callbackServer:");
+ }
+ if (token === null) {
+ token = prompt("getToken", "gap_callbackServer:");
+ }
+ xmlhttp.open("GET", "http://127.0.0.1:"+port+"/"+token , true);
+ xmlhttp.send();
+};
+
+module.exports = callback;
+});
+
+// file: lib/android/plugin/android/device.js
+define("cordova/plugin/android/device", function(require, exports, module) {
+var channel = require('cordova/channel'),
+ utils = require('cordova/utils'),
+ exec = require('cordova/exec'),
+ app = require('cordova/plugin/android/app');
+
+module.exports = {
+ /*
+ * DEPRECATED
+ * This is only for Android.
+ *
+ * You must explicitly override the back button.
+ */
+ overrideBackButton:function() {
+ console.log("Device.overrideBackButton() is deprecated. Use App.overrideBackbutton(true).");
+ app.overrideBackbutton(true);
+ },
+
+ /*
+ * DEPRECATED
+ * This is only for Android.
+ *
+ * This resets the back button to the default behaviour
+ */
+ resetBackButton:function() {
+ console.log("Device.resetBackButton() is deprecated. Use App.overrideBackbutton(false).");
+ app.overrideBackbutton(false);
+ },
+
+ /*
+ * DEPRECATED
+ * This is only for Android.
+ *
+ * This terminates the activity!
+ */
+ exitApp:function() {
+ console.log("Device.exitApp() is deprecated. Use App.exitApp().");
+ app.exitApp();
+ }
+};
+
+});
+
+// file: lib/android/plugin/android/notification.js
+define("cordova/plugin/android/notification", function(require, exports, module) {
+var exec = require('cordova/exec');
+
+/**
+ * Provides Android enhanced notification API.
+ */
+module.exports = {
+ activityStart : function(title, message) {
+ // If title and message not specified then mimic Android behavior of
+ // using default strings.
+ if (typeof title === "undefined" && typeof message == "undefined") {
+ title = "Busy";
+ message = 'Please wait...';
+ }
+
+ exec(null, null, 'Notification', 'activityStart', [ title, message ]);
+ },
+
+ /**
+ * Close an activity dialog
+ */
+ activityStop : function() {
+ exec(null, null, 'Notification', 'activityStop', []);
+ },
+
+ /**
+ * Display a progress dialog with progress bar that goes from 0 to 100.
+ *
+ * @param {String}
+ * title Title of the progress dialog.
+ * @param {String}
+ * message Message to display in the dialog.
+ */
+ progressStart : function(title, message) {
+ exec(null, null, 'Notification', 'progressStart', [ title, message ]);
+ },
+
+ /**
+ * Close the progress dialog.
+ */
+ progressStop : function() {
+ exec(null, null, 'Notification', 'progressStop', []);
+ },
+
+ /**
+ * Set the progress dialog value.
+ *
+ * @param {Number}
+ * value 0-100
+ */
+ progressValue : function(value) {
+ exec(null, null, 'Notification', 'progressValue', [ value ]);
+ }
+};
+});
+
+// file: lib/android/plugin/android/polling.js
+define("cordova/plugin/android/polling", function(require, exports, module) {
+var cordova = require('cordova'),
+ period = 50,
+ polling = function() {
+ // Exit if shutting down app
+ if (cordova.shuttingDown) {
+ return;
+ }
+
+ // If polling flag was changed, stop using polling from now on and switch to XHR server / callback
+ if (!cordova.UsePolling) {
+ require('cordova/plugin/android/callback')();
+ return;
+ }
+
+ var msg = prompt("", "gap_poll:");
+ if (msg) {
+ setTimeout(function() {
+ try {
+ var t = eval(""+msg);
+ }
+ catch (e) {
+ console.log("JSCallbackPolling: Message from Server: " + msg);
+ console.log("JSCallbackPolling Error: "+e);
+ }
+ }, 1);
+ setTimeout(polling, 1);
+ }
+ else {
+ setTimeout(polling, period);
+ }
+};
+
+module.exports = polling;
+});
+
+// file: lib/android/plugin/android/storage.js
+define("cordova/plugin/android/storage", function(require, exports, module) {
+var utils = require('cordova/utils'),
+ exec = require('cordova/exec'),
+ channel = require('cordova/channel');
+
+var queryQueue = {};
+
+/**
+ * SQL result set object
+ * PRIVATE METHOD
+ * @constructor
+ */
+var DroidDB_Rows = function() {
+ this.resultSet = []; // results array
+ this.length = 0; // number of rows
+};
+
+/**
+ * Get item from SQL result set
+ *
+ * @param row The row number to return
+ * @return The row object
+ */
+DroidDB_Rows.prototype.item = function(row) {
+ return this.resultSet[row];
+};
+
+/**
+ * SQL result set that is returned to user.
+ * PRIVATE METHOD
+ * @constructor
+ */
+var DroidDB_Result = function() {
+ this.rows = new DroidDB_Rows();
+};
+
+/**
+ * Callback from native code when query is complete.
+ * PRIVATE METHOD
+ *
+ * @param id Query id
+ */
+function completeQuery(id, data) {
+ var query = queryQueue[id];
+ if (query) {
+ try {
+ delete queryQueue[id];
+
+ // Get transaction
+ var tx = query.tx;
+
+ // If transaction hasn't failed
+ // Note: We ignore all query results if previous query
+ // in the same transaction failed.
+ if (tx && tx.queryList[id]) {
+
+ // Save query results
+ var r = new DroidDB_Result();
+ r.rows.resultSet = data;
+ r.rows.length = data.length;
+ try {
+ if (typeof query.successCallback === 'function') {
+ query.successCallback(query.tx, r);
+ }
+ } catch (ex) {
+ console.log("executeSql error calling user success callback: "+ex);
+ }
+
+ tx.queryComplete(id);
+ }
+ } catch (e) {
+ console.log("executeSql error: "+e);
+ }
+ }
+}
+
+/**
+ * Callback from native code when query fails
+ * PRIVATE METHOD
+ *
+ * @param reason Error message
+ * @param id Query id
+ */
+function failQuery(reason, id) {
+ var query = queryQueue[id];
+ if (query) {
+ try {
+ delete queryQueue[id];
+
+ // Get transaction
+ var tx = query.tx;
+
+ // If transaction hasn't failed
+ // Note: We ignore all query results if previous query
+ // in the same transaction failed.
+ if (tx && tx.queryList[id]) {
+ tx.queryList = {};
+
+ try {
+ if (typeof query.errorCallback === 'function') {
+ query.errorCallback(query.tx, reason);
+ }
+ } catch (ex) {
+ console.log("executeSql error calling user error callback: "+ex);
+ }
+
+ tx.queryFailed(id, reason);
+ }
+
+ } catch (e) {
+ console.log("executeSql error: "+e);
+ }
+ }
+}
+
+/**
+ * SQL query object
+ * PRIVATE METHOD
+ *
+ * @constructor
+ * @param tx The transaction object that this query belongs to
+ */
+var DroidDB_Query = function(tx) {
+
+ // Set the id of the query
+ this.id = utils.createUUID();
+
+ // Add this query to the queue
+ queryQueue[this.id] = this;
+
+ // Init result
+ this.resultSet = [];
+
+ // Set transaction that this query belongs to
+ this.tx = tx;
+
+ // Add this query to transaction list
+ this.tx.queryList[this.id] = this;
+
+ // Callbacks
+ this.successCallback = null;
+ this.errorCallback = null;
+
+};
+
+/**
+ * Transaction object
+ * PRIVATE METHOD
+ * @constructor
+ */
+var DroidDB_Tx = function() {
+
+ // Set the id of the transaction
+ this.id = utils.createUUID();
+
+ // Callbacks
+ this.successCallback = null;
+ this.errorCallback = null;
+
+ // Query list
+ this.queryList = {};
+};
+
+/**
+ * Mark query in transaction as complete.
+ * If all queries are complete, call the user's transaction success callback.
+ *
+ * @param id Query id
+ */
+DroidDB_Tx.prototype.queryComplete = function(id) {
+ delete this.queryList[id];
+
+ // If no more outstanding queries, then fire transaction success
+ if (this.successCallback) {
+ var count = 0;
+ var i;
+ for (i in this.queryList) {
+ if (this.queryList.hasOwnProperty(i)) {
+ count++;
+ }
+ }
+ if (count === 0) {
+ try {
+ this.successCallback();
+ } catch(e) {
+ console.log("Transaction error calling user success callback: " + e);
+ }
+ }
+ }
+};
+
+/**
+ * Mark query in transaction as failed.
+ *
+ * @param id Query id
+ * @param reason Error message
+ */
+DroidDB_Tx.prototype.queryFailed = function(id, reason) {
+
+ // The sql queries in this transaction have already been run, since
+ // we really don't have a real transaction implemented in native code.
+ // However, the user callbacks for the remaining sql queries in transaction
+ // will not be called.
+ this.queryList = {};
+
+ if (this.errorCallback) {
+ try {
+ this.errorCallback(reason);
+ } catch(e) {
+ console.log("Transaction error calling user error callback: " + e);
+ }
+ }
+};
+
+/**
+ * Execute SQL statement
+ *
+ * @param sql SQL statement to execute
+ * @param params Statement parameters
+ * @param successCallback Success callback
+ * @param errorCallback Error callback
+ */
+DroidDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCallback) {
+
+ // Init params array
+ if (typeof params === 'undefined') {
+ params = [];
+ }
+
+ // Create query and add to queue
+ var query = new DroidDB_Query(this);
+ queryQueue[query.id] = query;
+
+ // Save callbacks
+ query.successCallback = successCallback;
+ query.errorCallback = errorCallback;
+
+ // Call native code
+ exec(null, null, "Storage", "executeSql", [sql, params, query.id]);
+};
+
+var DatabaseShell = function() {
+};
+
+/**
+ * Start a transaction.
+ * Does not support rollback in event of failure.
+ *
+ * @param process {Function} The transaction function
+ * @param successCallback {Function}
+ * @param errorCallback {Function}
+ */
+DatabaseShell.prototype.transaction = function(process, errorCallback, successCallback) {
+ var tx = new DroidDB_Tx();
+ tx.successCallback = successCallback;
+ tx.errorCallback = errorCallback;
+ try {
+ process(tx);
+ } catch (e) {
+ console.log("Transaction error: "+e);
+ if (tx.errorCallback) {
+ try {
+ tx.errorCallback(e);
+ } catch (ex) {
+ console.log("Transaction error calling user error callback: "+e);
+ }
+ }
+ }
+};
+
+/**
+ * Open database
+ *
+ * @param name Database name
+ * @param version Database version
+ * @param display_name Database display name
+ * @param size Database size in bytes
+ * @return Database object
+ */
+var DroidDB_openDatabase = function(name, version, display_name, size) {
+ exec(null, null, "Storage", "openDatabase", [name, version, display_name, size]);
+ var db = new DatabaseShell();
+ return db;
+};
+
+/**
+ * For browsers with no localStorage we emulate it with SQLite. Follows the w3c api.
+ * TODO: Do similar for sessionStorage.
+ * @constructor
+ */
+var CupcakeLocalStorage = function() {
+ channel.waitForInitialization("cupcakeStorage");
+
+ try {
+
+ this.db = openDatabase('localStorage', '1.0', 'localStorage', 2621440);
+ var storage = {};
+ this.length = 0;
+ function setLength (length) {
+ this.length = length;
+ localStorage.length = length;
+ }
+ this.db.transaction(
+ function (transaction) {
+ var i;
+ transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
+ transaction.executeSql('SELECT * FROM storage', [], function(tx, result) {
+ for(var i = 0; i < result.rows.length; i++) {
+ storage[result.rows.item(i).id] = result.rows.item(i).body;
+ }
+ setLength(result.rows.length);
+ channel.initializationComplete("cupcakeStorage");
+ });
+
+ },
+ function (err) {
+ utils.alert(err.message);
+ }
+ );
+ this.setItem = function(key, val) {
+ if (typeof(storage[key])=='undefined') {
+ this.length++;
+ }
+ storage[key] = val;
+ this.db.transaction(
+ function (transaction) {
+ transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
+ transaction.executeSql('REPLACE INTO storage (id, body) values(?,?)', [key,val]);
+ }
+ );
+ };
+ this.getItem = function(key) {
+ return storage[key];
+ };
+ this.removeItem = function(key) {
+ delete storage[key];
+ this.length--;
+ this.db.transaction(
+ function (transaction) {
+ transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
+ transaction.executeSql('DELETE FROM storage where id=?', [key]);
+ }
+ );
+ };
+ this.clear = function() {
+ storage = {};
+ this.length = 0;
+ this.db.transaction(
+ function (transaction) {
+ transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
+ transaction.executeSql('DELETE FROM storage', []);
+ }
+ );
+ };
+ this.key = function(index) {
+ var i = 0;
+ for (var j in storage) {
+ if (i==index) {
+ return j;
+ } else {
+ i++;
+ }
+ }
+ return null;
+ };
+
+ } catch(e) {
+ utils.alert("Database error "+e+".");
+ return;
+ }
+};
+
+module.exports = {
+ openDatabase:DroidDB_openDatabase,
+ CupcakeLocalStorage:CupcakeLocalStorage,
+ failQuery:failQuery,
+ completeQuery:completeQuery
+};
+
+});
+
+// file: lib/common/plugin/battery.js
+define("cordova/plugin/battery", function(require, exports, module) {
+/**
+ * This class contains information about the current battery status.
+ * @constructor
+ */
+var cordova = require('cordova'),
+ exec = require('cordova/exec');
+
+function handlers() {
+ return battery.channels.batterystatus.numHandlers +
+ battery.channels.batterylow.numHandlers +
+ battery.channels.batterycritical.numHandlers;
+}
+
+var Battery = function() {
+ this._level = null;
+ this._isPlugged = null;
+ // Create new event handlers on the window (returns a channel instance)
+ var subscriptionEvents = {
+ onSubscribe:this.onSubscribe,
+ onUnsubscribe:this.onUnsubscribe
+ };
+ this.channels = {
+ batterystatus:cordova.addWindowEventHandler("batterystatus", subscriptionEvents),
+ batterylow:cordova.addWindowEventHandler("batterylow", subscriptionEvents),
+ batterycritical:cordova.addWindowEventHandler("batterycritical", subscriptionEvents)
+ };
+};
+/**
+ * Event handlers for when callbacks get registered for the battery.
+ * Keep track of how many handlers we have so we can start and stop the native battery listener
+ * appropriately (and hopefully save on battery life!).
+ */
+Battery.prototype.onSubscribe = function() {
+ var me = battery;
+ // If we just registered the first handler, make sure native listener is started.
+ if (handlers() === 1) {
+ exec(me._status, me._error, "Battery", "start", []);
+ }
+};
+
+Battery.prototype.onUnsubscribe = function() {
+ var me = battery;
+
+ // If we just unregistered the last handler, make sure native listener is stopped.
+ if (handlers() === 0) {
+ exec(null, null, "Battery", "stop", []);
+ }
+};
+
+/**
+ * Callback for battery status
+ *
+ * @param {Object} info keys: level, isPlugged
+ */
+Battery.prototype._status = function(info) {
+ if (info) {
+ var me = battery;
+ var level = info.level;
+ if (me._level !== level || me._isPlugged !== info.isPlugged) {
+ // Fire batterystatus event
+ cordova.fireWindowEvent("batterystatus", info);
+
+ // Fire low battery event
+ if (level === 20 || level === 5) {
+ if (level === 20) {
+ cordova.fireWindowEvent("batterylow", info);
+ }
+ else {
+ cordova.fireWindowEvent("batterycritical", info);
+ }
+ }
+ }
+ me._level = level;
+ me._isPlugged = info.isPlugged;
+ }
+};
+
+/**
+ * Error callback for battery start
+ */
+Battery.prototype._error = function(e) {
+ console.log("Error initializing Battery: " + e);
+};
+
+var battery = new Battery();
+
+module.exports = battery;
+});
+
+// file: lib/common/plugin/capture.js
+define("cordova/plugin/capture", function(require, exports, module) {
+var exec = require('cordova/exec'),
+ MediaFile = require('cordova/plugin/MediaFile');
+
+/**
+ * Launches a capture of different types.
+ *
+ * @param (DOMString} type
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ * @param {CaptureVideoOptions} options
+ */
+function _capture(type, successCallback, errorCallback, options) {
+ var win = function(pluginResult) {
+ var mediaFiles = [];
+ var i;
+ for (i = 0; i < pluginResult.length; i++) {
+ var mediaFile = new MediaFile();
+ mediaFile.name = pluginResult[i].name;
+ mediaFile.fullPath = pluginResult[i].fullPath;
+ mediaFile.type = pluginResult[i].type;
+ mediaFile.lastModifiedDate = pluginResult[i].lastModifiedDate;
+ mediaFile.size = pluginResult[i].size;
+ mediaFiles.push(mediaFile);
+ }
+ successCallback(mediaFiles);
+ };
+ exec(win, errorCallback, "Capture", type, [options]);
+}
+/**
+ * The Capture interface exposes an interface to the camera and microphone of the hosting device.
+ */
+function Capture() {
+ this.supportedAudioModes = [];
+ this.supportedImageModes = [];
+ this.supportedVideoModes = [];
+}
+
+/**
+ * Launch audio recorder application for recording audio clip(s).
+ *
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ * @param {CaptureAudioOptions} options
+ */
+Capture.prototype.captureAudio = function(successCallback, errorCallback, options){
+ _capture("captureAudio", successCallback, errorCallback, options);
+};
+
+/**
+ * Launch camera application for taking image(s).
+ *
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ * @param {CaptureImageOptions} options
+ */
+Capture.prototype.captureImage = function(successCallback, errorCallback, options){
+ _capture("captureImage", successCallback, errorCallback, options);
+};
+
+/**
+ * Launch device camera application for recording video(s).
+ *
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ * @param {CaptureVideoOptions} options
+ */
+Capture.prototype.captureVideo = function(successCallback, errorCallback, options){
+ _capture("captureVideo", successCallback, errorCallback, options);
+};
+
+
+module.exports = new Capture();
+
+});
+
+// file: lib/common/plugin/compass.js
+define("cordova/plugin/compass", function(require, exports, module) {
+var exec = require('cordova/exec'),
+ utils = require('cordova/utils'),
+ CompassHeading = require('cordova/plugin/CompassHeading'),
+ CompassError = require('cordova/plugin/CompassError'),
+ timers = {},
+ compass = {
+ /**
+ * Asynchronously acquires the current heading.
+ * @param {Function} successCallback The function to call when the heading
+ * data is available
+ * @param {Function} errorCallback The function to call when there is an error
+ * getting the heading data.
+ * @param {CompassOptions} options The options for getting the heading data (not used).
+ */
+ getCurrentHeading:function(successCallback, errorCallback, options) {
+ // successCallback required
+ if (typeof successCallback !== "function") {
+ console.log("Compass Error: successCallback is not a function");
+ return;
+ }
+
+ // errorCallback optional
+ if (errorCallback && (typeof errorCallback !== "function")) {
+ console.log("Compass Error: errorCallback is not a function");
+ return;
+ }
+
+ var win = function(result) {
+ var ch = new CompassHeading(result.magneticHeading, result.trueHeading, result.headingAccuracy, result.timestamp);
+ successCallback(ch);
+ };
+ var fail = function(code) {
+ var ce = new CompassError(code);
+ errorCallback(ce);
+ };
+
+ // Get heading
+ exec(win, fail, "Compass", "getHeading", [options]);
+ },
+
+ /**
+ * Asynchronously acquires the heading repeatedly at a given interval.
+ * @param {Function} successCallback The function to call each time the heading
+ * data is available
+ * @param {Function} errorCallback The function to call when there is an error
+ * getting the heading data.
+ * @param {HeadingOptions} options The options for getting the heading data
+ * such as timeout and the frequency of the watch. For iOS, filter parameter
+ * specifies to watch via a distance filter rather than time.
+ */
+ watchHeading:function(successCallback, errorCallback, options) {
+ // Default interval (100 msec)
+ var frequency = (options !== undefined && options.frequency !== undefined) ? options.frequency : 100;
+ var filter = (options !== undefined && options.filter !== undefined) ? options.filter : 0;
+
+ // successCallback required
+ if (typeof successCallback !== "function") {
+ console.log("Compass Error: successCallback is not a function");
+ return;
+ }
+
+ // errorCallback optional
+ if (errorCallback && (typeof errorCallback !== "function")) {
+ console.log("Compass Error: errorCallback is not a function");
+ return;
+ }
+
+ var id = utils.createUUID();
+ if (filter > 0) {
+ // is an iOS request for watch by filter, no timer needed
+ timers[id] = "iOS";
+ compass.getCurrentHeading(successCallback, errorCallback, options);
+ } else {
+ // Start watch timer to get headings
+ timers[id] = window.setInterval(function() {
+ compass.getCurrentHeading(successCallback, errorCallback);
+ }, frequency);
+ }
+
+ return id;
+ },
+
+ /**
+ * Clears the specified heading watch.
+ * @param {String} watchId The ID of the watch returned from #watchHeading.
+ */
+ clearWatch:function(id) {
+ // Stop javascript timer & remove from timer list
+ if (id && timers[id]) {
+ if (timers[id] != "iOS") {
+ clearInterval(timers[id]);
+ } else {
+ // is iOS watch by filter so call into device to stop
+ exec(null, null, "Compass", "stopHeading", []);
+ }
+ delete timers[id];
+ }
+ }
+ };
+
+module.exports = compass;
+});
+
+// file: lib/common/plugin/console-via-logger.js
+define("cordova/plugin/console-via-logger", function(require, exports, module) {
+//------------------------------------------------------------------------------
+
+var logger = require("cordova/plugin/logger");
+var utils = require("cordova/utils");
+
+//------------------------------------------------------------------------------
+// object that we're exporting
+//------------------------------------------------------------------------------
+var console = module.exports;
+
+//------------------------------------------------------------------------------
+// copy of the original console object
+//------------------------------------------------------------------------------
+var WinConsole = window.console;
+
+//------------------------------------------------------------------------------
+// whether to use the logger
+//------------------------------------------------------------------------------
+var UseLogger = false;
+
+//------------------------------------------------------------------------------
+// Timers
+//------------------------------------------------------------------------------
+var Timers = {};
+
+//------------------------------------------------------------------------------
+// used for unimplemented methods
+//------------------------------------------------------------------------------
+function noop() {}
+
+//------------------------------------------------------------------------------
+// used for unimplemented methods
+//------------------------------------------------------------------------------
+console.useLogger = function (value) {
+ if (arguments.length) UseLogger = !!value;
+
+ if (UseLogger) {
+ if (logger.useConsole()) {
+ throw new Error("console and logger are too intertwingly");
+ }
+ }
+
+ return UseLogger;
+};
+
+//------------------------------------------------------------------------------
+console.log = function() {
+ if (logger.useConsole()) return;
+ logger.log.apply(logger, [].slice.call(arguments));
+};
+
+//------------------------------------------------------------------------------
+console.error = function() {
+ if (logger.useConsole()) return;
+ logger.error.apply(logger, [].slice.call(arguments));
+};
+
+//------------------------------------------------------------------------------
+console.warn = function() {
+ if (logger.useConsole()) return;
+ logger.warn.apply(logger, [].slice.call(arguments));
+};
+
+//------------------------------------------------------------------------------
+console.info = function() {
+ if (logger.useConsole()) return;
+ logger.info.apply(logger, [].slice.call(arguments));
+};
+
+//------------------------------------------------------------------------------
+console.debug = function() {
+ if (logger.useConsole()) return;
+ logger.debug.apply(logger, [].slice.call(arguments));
+};
+
+//------------------------------------------------------------------------------
+console.assert = function(expression) {
+ if (expression) return;
+
+ var message = utils.vformat(arguments[1], [].slice.call(arguments, 2));
+ console.log("ASSERT: " + message);
+};
+
+//------------------------------------------------------------------------------
+console.clear = function() {};
+
+//------------------------------------------------------------------------------
+console.dir = function(object) {
+ console.log("%o", object);
+};
+
+//------------------------------------------------------------------------------
+console.dirxml = function(node) {
+ console.log(node.innerHTML);
+};
+
+//------------------------------------------------------------------------------
+console.trace = noop;
+
+//------------------------------------------------------------------------------
+console.group = console.log;
+
+//------------------------------------------------------------------------------
+console.groupCollapsed = console.log;
+
+//------------------------------------------------------------------------------
+console.groupEnd = noop;
+
+//------------------------------------------------------------------------------
+console.time = function(name) {
+ Timers[name] = new Date().valueOf();
+};
+
+//------------------------------------------------------------------------------
+console.timeEnd = function(name) {
+ var timeStart = Timers[name];
+ if (!timeStart) {
+ console.warn("unknown timer: " + name);
+ return;
+ }
+
+ var timeElapsed = new Date().valueOf() - timeStart;
+ console.log(name + ": " + timeElapsed + "ms");
+};
+
+//------------------------------------------------------------------------------
+console.timeStamp = noop;
+
+//------------------------------------------------------------------------------
+console.profile = noop;
+
+//------------------------------------------------------------------------------
+console.profileEnd = noop;
+
+//------------------------------------------------------------------------------
+console.count = noop;
+
+//------------------------------------------------------------------------------
+console.exception = console.log;
+
+//------------------------------------------------------------------------------
+console.table = function(data, columns) {
+ console.log("%o", data);
+};
+
+//------------------------------------------------------------------------------
+// return a new function that calls both functions passed as args
+//------------------------------------------------------------------------------
+function wrapperedOrigCall(orgFunc, newFunc) {
+ return function() {
+ var args = [].slice.call(arguments);
+ try { orgFunc.apply(WinConsole, args); } catch (e) {}
+ try { newFunc.apply(console, args); } catch (e) {}
+ };
+}
+
+//------------------------------------------------------------------------------
+// For every function that exists in the original console object, that
+// also exists in the new console object, wrap the new console method
+// with one that calls both
+//------------------------------------------------------------------------------
+for (var key in console) {
+ if (typeof WinConsole[key] == "function") {
+ console[key] = wrapperedOrigCall(WinConsole[key], console[key]);
+ }
+}
+
+});
+
+// file: lib/common/plugin/contacts.js
+define("cordova/plugin/contacts", function(require, exports, module) {
+var exec = require('cordova/exec'),
+ ContactError = require('cordova/plugin/ContactError'),
+ utils = require('cordova/utils'),
+ Contact = require('cordova/plugin/Contact');
+
+/**
+* Represents a group of Contacts.
+* @constructor
+*/
+var contacts = {
+ /**
+ * Returns an array of Contacts matching the search criteria.
+ * @param fields that should be searched
+ * @param successCB success callback
+ * @param errorCB error callback
+ * @param {ContactFindOptions} options that can be applied to contact searching
+ * @return array of Contacts matching search criteria
+ */
+ find:function(fields, successCB, errorCB, options) {
+ if (!successCB) {
+ throw new TypeError("You must specify a success callback for the find command.");
+ }
+ if (!fields || (utils.isArray(fields) && fields.length === 0)) {
+ if (typeof errorCB === "function") {
+ errorCB(new ContactError(ContactError.INVALID_ARGUMENT_ERROR));
+ }
+ } else {
+ var win = function(result) {
+ var cs = [];
+ for (var i = 0, l = result.length; i < l; i++) {
+ cs.push(contacts.create(result[i]));
+ }
+ successCB(cs);
+ };
+ exec(win, errorCB, "Contacts", "search", [fields, options]);
+ }
+ },
+
+ /**
+ * This function creates a new contact, but it does not persist the contact
+ * to device storage. To persist the contact to device storage, invoke
+ * contact.save().
+ * @param properties an object who's properties will be examined to create a new Contact
+ * @returns new Contact object
+ */
+ create:function(properties) {
+ var i;
+ var contact = new Contact();
+ for (i in properties) {
+ if (typeof contact[i] !== 'undefined' && properties.hasOwnProperty(i)) {
+ contact[i] = properties[i];
+ }
+ }
+ return contact;
+ }
+};
+
+module.exports = contacts;
+
+});
+
+// file: lib/common/plugin/device.js
+define("cordova/plugin/device", function(require, exports, module) {
+var channel = require('cordova/channel'),
+ utils = require('cordova/utils'),
+ exec = require('cordova/exec');
+
+// Tell cordova channel to wait on the CordovaInfoReady event
+channel.waitForInitialization('onCordovaInfoReady');
+
+/**
+ * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the
+ * phone, etc.
+ * @constructor
+ */
+function Device() {
+ this.available = false;
+ this.platform = null;
+ this.version = null;
+ this.name = null;
+ this.uuid = null;
+ this.cordova = null;
+
+ var me = this;
+
+ channel.onCordovaReady.subscribeOnce(function() {
+ me.getInfo(function(info) {
+ me.available = true;
+ me.platform = info.platform;
+ me.version = info.version;
+ me.name = info.name;
+ me.uuid = info.uuid;
+ me.cordova = info.cordova;
+ channel.onCordovaInfoReady.fire();
+ },function(e) {
+ me.available = false;
+ utils.alert("[ERROR] Error initializing Cordova: " + e);
+ });
+ });
+}
+
+/**
+ * Get device info
+ *
+ * @param {Function} successCallback The function to call when the heading data is available
+ * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL)
+ */
+Device.prototype.getInfo = function(successCallback, errorCallback) {
+
+ // successCallback required
+ if (typeof successCallback !== "function") {
+ console.log("Device Error: successCallback is not a function");
+ return;
+ }
+
+ // errorCallback optional
+ if (errorCallback && (typeof errorCallback !== "function")) {
+ console.log("Device Error: errorCallback is not a function");
+ return;
+ }
+
+ // Get info
+ exec(successCallback, errorCallback, "Device", "getDeviceInfo", []);
+};
+
+module.exports = new Device();
+
+});
+
+// file: lib/common/plugin/geolocation.js
+define("cordova/plugin/geolocation", function(require, exports, module) {
+var utils = require('cordova/utils'),
+ exec = require('cordova/exec'),
+ PositionError = require('cordova/plugin/PositionError'),
+ Position = require('cordova/plugin/Position');
+
+var timers = {}; // list of timers in use
+
+// Returns default params, overrides if provided with values
+function parseParameters(options) {
+ var opt = {
+ maximumAge: 0,
+ enableHighAccuracy: false,
+ timeout: Infinity
+ };
+
+ if (options) {
+ if (options.maximumAge !== undefined && !isNaN(options.maximumAge) && options.maximumAge > 0) {
+ opt.maximumAge = options.maximumAge;
+ }
+ if (options.enableHighAccuracy !== undefined) {
+ opt.enableHighAccuracy = options.enableHighAccuracy;
+ }
+ if (options.timeout !== undefined && !isNaN(options.timeout)) {
+ if (options.timeout < 0) {
+ opt.timeout = 0;
+ } else {
+ opt.timeout = options.timeout;
+ }
+ }
+ }
+
+ return opt;
+}
+
+// Returns a timeout failure, closed over a specified timeout value and error callback.
+function createTimeout(errorCallback, timeout) {
+ var t = setTimeout(function() {
+ clearTimeout(t);
+ t = null;
+ errorCallback({
+ code:PositionError.TIMEOUT,
+ message:"Position retrieval timed out."
+ });
+ }, timeout);
+ return t;
+}
+
+var geolocation = {
+ lastPosition:null, // reference to last known (cached) position returned
+ /**
+ * Asynchronously aquires the current position.
+ *
+ * @param {Function} successCallback The function to call when the position data is available
+ * @param {Function} errorCallback The function to call when there is an error getting the heading position. (OPTIONAL)
+ * @param {PositionOptions} options The options for getting the position data. (OPTIONAL)
+ */
+ getCurrentPosition:function(successCallback, errorCallback, options) {
+ if (arguments.length === 0) {
+ throw new Error("getCurrentPosition must be called with at least one argument.");
+ }
+ options = parseParameters(options);
+
+ // Timer var that will fire an error callback if no position is retrieved from native
+ // before the "timeout" param provided expires
+ var timeoutTimer = null;
+
+ var win = function(p) {
+ clearTimeout(timeoutTimer);
+ if (!timeoutTimer) {
+ // Timeout already happened, or native fired error callback for
+ // this geo request.
+ // Don't continue with success callback.
+ return;
+ }
+ var pos = new Position(
+ {
+ latitude:p.latitude,
+ longitude:p.longitude,
+ altitude:p.altitude,
+ accuracy:p.accuracy,
+ heading:p.heading,
+ velocity:p.velocity,
+ altitudeAccuracy:p.altitudeAccuracy
+ },
+ (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp)))
+ );
+ geolocation.lastPosition = pos;
+ successCallback(pos);
+ };
+ var fail = function(e) {
+ clearTimeout(timeoutTimer);
+ timeoutTimer = null;
+ var err = new PositionError(e.code, e.message);
+ if (errorCallback) {
+ errorCallback(err);
+ }
+ };
+
+ // Check our cached position, if its timestamp difference with current time is less than the maximumAge, then just
+ // fire the success callback with the cached position.
+ if (geolocation.lastPosition && options.maximumAge && (((new Date()).getTime() - geolocation.lastPosition.timestamp.getTime()) <= options.maximumAge)) {
+ successCallback(geolocation.lastPosition);
+ // If the cached position check failed and the timeout was set to 0, error out with a TIMEOUT error object.
+ } else if (options.timeout === 0) {
+ fail({
+ code:PositionError.TIMEOUT,
+ message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceed's provided PositionOptions' maximumAge parameter."
+ });
+ // Otherwise we have to call into native to retrieve a position.
+ } else {
+ if (options.timeout !== Infinity) {
+ // If the timeout value was not set to Infinity (default), then
+ // set up a timeout function that will fire the error callback
+ // if no successful position was retrieved before timeout expired.
+ timeoutTimer = createTimeout(fail, options.timeout);
+ } else {
+ // This is here so the check in the win function doesn't mess stuff up
+ // may seem weird but this guarantees timeoutTimer is
+ // always truthy before we call into native
+ timeoutTimer = true;
+ }
+ exec(win, fail, "Geolocation", "getLocation", [options.enableHighAccuracy, options.maximumAge]);
+ }
+ return timeoutTimer;
+ },
+ /**
+ * Asynchronously watches the geolocation for changes to geolocation. When a change occurs,
+ * the successCallback is called with the new location.
+ *
+ * @param {Function} successCallback The function to call each time the location data is available
+ * @param {Function} errorCallback The function to call when there is an error getting the location data. (OPTIONAL)
+ * @param {PositionOptions} options The options for getting the location data such as frequency. (OPTIONAL)
+ * @return String The watch id that must be passed to #clearWatch to stop watching.
+ */
+ watchPosition:function(successCallback, errorCallback, options) {
+ if (arguments.length === 0) {
+ throw new Error("watchPosition must be called with at least one argument.");
+ }
+ options = parseParameters(options);
+
+ var id = utils.createUUID();
+
+ // Tell device to get a position ASAP, and also retrieve a reference to the timeout timer generated in getCurrentPosition
+ timers[id] = geolocation.getCurrentPosition(successCallback, errorCallback, options);
+
+ var fail = function(e) {
+ clearTimeout(timers[id]);
+ var err = new PositionError(e.code, e.message);
+ if (errorCallback) {
+ errorCallback(err);
+ }
+ };
+
+ var win = function(p) {
+ clearTimeout(timers[id]);
+ if (options.timeout !== Infinity) {
+ timers[id] = createTimeout(fail, options.timeout);
+ }
+ var pos = new Position(
+ {
+ latitude:p.latitude,
+ longitude:p.longitude,
+ altitude:p.altitude,
+ accuracy:p.accuracy,
+ heading:p.heading,
+ velocity:p.velocity,
+ altitudeAccuracy:p.altitudeAccuracy
+ },
+ (p.timestamp === undefined ? new Date() : ((p.timestamp instanceof Date) ? p.timestamp : new Date(p.timestamp)))
+ );
+ geolocation.lastPosition = pos;
+ successCallback(pos);
+ };
+
+ exec(win, fail, "Geolocation", "addWatch", [id, options.enableHighAccuracy]);
+
+ return id;
+ },
+ /**
+ * Clears the specified heading watch.
+ *
+ * @param {String} id The ID of the watch returned from #watchPosition
+ */
+ clearWatch:function(id) {
+ if (id && timers[id] !== undefined) {
+ clearTimeout(timers[id]);
+ delete timers[id];
+ exec(null, null, "Geolocation", "clearWatch", [id]);
+ }
+ }
+};
+
+module.exports = geolocation;
+
+});
+
+// file: lib/common/plugin/logger.js
+define("cordova/plugin/logger", function(require, exports, module) {
+//------------------------------------------------------------------------------
+// The logger module exports the following properties/functions:
+//
+// LOG - constant for the level LOG
+// ERROR - constant for the level ERROR
+// WARN - constant for the level WARN
+// INFO - constant for the level INFO
+// DEBUG - constant for the level DEBUG
+// logLevel() - returns current log level
+// logLevel(value) - sets and returns a new log level
+// useConsole() - returns whether logger is using console
+// useConsole(value) - sets and returns whether logger is using console
+// log(message,...) - logs a message at level LOG
+// error(message,...) - logs a message at level ERROR
+// warn(message,...) - logs a message at level WARN
+// info(message,...) - logs a message at level INFO
+// debug(message,...) - logs a message at level DEBUG
+// logLevel(level,message,...) - logs a message specified level
+//
+//------------------------------------------------------------------------------
+
+var logger = exports;
+
+var exec = require('cordova/exec');
+var utils = require('cordova/utils');
+
+var UseConsole = true;
+var Queued = [];
+var DeviceReady = false;
+var CurrentLevel;
+
+/**
+ * Logging levels
+ */
+
+var Levels = [
+ "LOG",
+ "ERROR",
+ "WARN",
+ "INFO",
+ "DEBUG"
+];
+
+/*
+ * add the logging levels to the logger object and
+ * to a separate levelsMap object for testing
+ */
+
+var LevelsMap = {};
+for (var i=0; i CurrentLevel) return;
+
+ // queue the message if not yet at deviceready
+ if (!DeviceReady && !UseConsole) {
+ Queued.push([level, message]);
+ return;
+ }
+
+ // if not using the console, use the native logger
+ if (!UseConsole) {
+ exec(null, null, "Logger", "logLevel", [level, message]);
+ return;
+ }
+
+ // make sure console is not using logger
+ if (console.__usingCordovaLogger) {
+ throw new Error("console and logger are too intertwingly");
+ }
+
+ // log to the console
+ switch (level) {
+ case logger.LOG: console.log(message); break;
+ case logger.ERROR: console.log("ERROR: " + message); break;
+ case logger.WARN: console.log("WARN: " + message); break;
+ case logger.INFO: console.log("INFO: " + message); break;
+ case logger.DEBUG: console.log("DEBUG: " + message); break;
+ }
+};
+
+// when deviceready fires, log queued messages
+logger.__onDeviceReady = function() {
+ if (DeviceReady) return;
+
+ DeviceReady = true;
+
+ for (var i=0; i 3) {
+ fail(FileError.SYNTAX_ERR);
+ } else {
+ // if successful, return a FileSystem object
+ var success = function(file_system) {
+ if (file_system) {
+ if (typeof successCallback === 'function') {
+ // grab the name and root from the file system object
+ var result = new FileSystem(file_system.name, file_system.root);
+ successCallback(result);
+ }
+ }
+ else {
+ // no FileSystem object returned
+ fail(FileError.NOT_FOUND_ERR);
+ }
+ };
+ exec(success, fail, "File", "requestFileSystem", [type, size]);
+ }
+};
+
+module.exports = requestFileSystem;
+});
+
+// file: lib/common/plugin/resolveLocalFileSystemURI.js
+define("cordova/plugin/resolveLocalFileSystemURI", function(require, exports, module) {
+var DirectoryEntry = require('cordova/plugin/DirectoryEntry'),
+ FileEntry = require('cordova/plugin/FileEntry'),
+ FileError = require('cordova/plugin/FileError'),
+ exec = require('cordova/exec');
+
+/**
+ * Look up file system Entry referred to by local URI.
+ * @param {DOMString} uri URI referring to a local file or directory
+ * @param successCallback invoked with Entry object corresponding to URI
+ * @param errorCallback invoked if error occurs retrieving file system entry
+ */
+module.exports = function(uri, successCallback, errorCallback) {
+ // error callback
+ var fail = function(error) {
+ if (typeof errorCallback === 'function') {
+ errorCallback(new FileError(error));
+ }
+ };
+ // sanity check for 'not:valid:filename'
+ if(!uri || uri.split(":").length > 2) {
+ setTimeout( function() {
+ fail(FileError.ENCODING_ERR);
+ },0);
+ return;
+ }
+ // if successful, return either a file or directory entry
+ var success = function(entry) {
+ var result;
+ if (entry) {
+ if (typeof successCallback === 'function') {
+ // create appropriate Entry object
+ result = (entry.isDirectory) ? new DirectoryEntry(entry.name, entry.fullPath) : new FileEntry(entry.name, entry.fullPath);
+ try {
+ successCallback(result);
+ }
+ catch (e) {
+ console.log('Error invoking callback: ' + e);
+ }
+ }
+ }
+ else {
+ // no Entry object returned
+ fail(FileError.NOT_FOUND_ERR);
+ }
+ };
+
+ exec(success, fail, "File", "resolveLocalFileSystemURI", [uri]);
+};
+
+});
+
+// file: lib/common/plugin/splashscreen.js
+define("cordova/plugin/splashscreen", function(require, exports, module) {
+var exec = require('cordova/exec');
+
+var splashscreen = {
+ show:function() {
+ exec(null, null, "SplashScreen", "show", []);
+ },
+ hide:function() {
+ exec(null, null, "SplashScreen", "hide", []);
+ }
+};
+
+module.exports = splashscreen;
+});
+
+// file: lib/common/utils.js
+define("cordova/utils", function(require, exports, module) {
+var utils = exports;
+
+/**
+ * Returns an indication of whether the argument is an array or not
+ */
+utils.isArray = function(a) {
+ return Object.prototype.toString.call(a) == '[object Array]';
+};
+
+/**
+ * Returns an indication of whether the argument is a Date or not
+ */
+utils.isDate = function(d) {
+ return Object.prototype.toString.call(d) == '[object Date]';
+};
+
+/**
+ * Does a deep clone of the object.
+ */
+utils.clone = function(obj) {
+ if(!obj || typeof obj == 'function' || utils.isDate(obj) || typeof obj != 'object') {
+ return obj;
+ }
+
+ var retVal, i;
+
+ if(utils.isArray(obj)){
+ retVal = [];
+ for(i = 0; i < obj.length; ++i){
+ retVal.push(utils.clone(obj[i]));
+ }
+ return retVal;
+ }
+
+ retVal = {};
+ for(i in obj){
+ if(!(i in retVal) || retVal[i] != obj[i]) {
+ retVal[i] = utils.clone(obj[i]);
+ }
+ }
+ return retVal;
+};
+
+/**
+ * Returns a wrappered version of the function
+ */
+utils.close = function(context, func, params) {
+ if (typeof params == 'undefined') {
+ return function() {
+ return func.apply(context, arguments);
+ };
+ } else {
+ return function() {
+ return func.apply(context, params);
+ };
+ }
+};
+
+/**
+ * Create a UUID
+ */
+utils.createUUID = function() {
+ return UUIDcreatePart(4) + '-' +
+ UUIDcreatePart(2) + '-' +
+ UUIDcreatePart(2) + '-' +
+ UUIDcreatePart(2) + '-' +
+ UUIDcreatePart(6);
+};
+
+/**
+ * Extends a child object from a parent object using classical inheritance
+ * pattern.
+ */
+utils.extend = (function() {
+ // proxy used to establish prototype chain
+ var F = function() {};
+ // extend Child from Parent
+ return function(Child, Parent) {
+ F.prototype = Parent.prototype;
+ Child.prototype = new F();
+ Child.__super__ = Parent.prototype;
+ Child.prototype.constructor = Child;
+ };
+}());
+
+/**
+ * Alerts a message in any available way: alert or console.log.
+ */
+utils.alert = function(msg) {
+ if (alert) {
+ alert(msg);
+ } else if (console && console.log) {
+ console.log(msg);
+ }
+};
+
+/**
+ * Formats a string and arguments following it ala sprintf()
+ *
+ * see utils.vformat() for more information
+ */
+utils.format = function(formatString /* ,... */) {
+ var args = [].slice.call(arguments, 1);
+ return utils.vformat(formatString, args);
+};
+
+/**
+ * Formats a string and arguments following it ala vsprintf()
+ *
+ * format chars:
+ * %j - format arg as JSON
+ * %o - format arg as JSON
+ * %c - format arg as ''
+ * %% - replace with '%'
+ * any other char following % will format it's
+ * arg via toString().
+ *
+ * for rationale, see FireBug's Console API:
+ * http://getfirebug.com/wiki/index.php/Console_API
+ */
+utils.vformat = function(formatString, args) {
+ if (formatString === null || formatString === undefined) return "";
+ if (arguments.length == 1) return formatString.toString();
+ if (typeof formatString != "string") return formatString.toString();
+
+ var pattern = /(.*?)%(.)(.*)/;
+ var rest = formatString;
+ var result = [];
+
+ while (args.length) {
+ var arg = args.shift();
+ var match = pattern.exec(rest);
+
+ if (!match) break;
+
+ rest = match[3];
+
+ result.push(match[1]);
+
+ if (match[2] == '%') {
+ result.push('%');
+ args.unshift(arg);
+ continue;
+ }
+
+ result.push(formatted(arg, match[2]));
+ }
+
+ result.push(rest);
+
+ return result.join('');
+};
+
+//------------------------------------------------------------------------------
+function UUIDcreatePart(length) {
+ var uuidpart = "";
+ for (var i=0; i
+
+
+
+
+
+
+ PhoneGap and Wikitude
+
+
+
+
+
+
+
+ Hello Cordova
+
+
+
+
Apache Cordovaâ„¢
+
+
Connecting to Device
+
Device is Ready
+
+
+
+
+
+
+
+
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/js/WikitudePlugin.js b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/js/WikitudePlugin.js
new file mode 100644
index 0000000..bd09eb2
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/js/WikitudePlugin.js
@@ -0,0 +1,383 @@
+var WikitudePlugin = {
+
+ /**
+ *
+ * This is the SDK Key, provided to you after you purchased the Wikitude SDK from http://www.wikitude.com/developer/sdk
+ * If you're having a trial version, leave this string empty
+ *
+ */
+ mySDKKey : "ENTER-YOUR-KEY-HERE",
+
+ /** true if architectview is open */
+ isOpened : false,
+
+ /**
+ *
+ * called when user pressed back-button on Android device
+ *
+ */
+ backButtonCallback : null,
+
+ /**
+ *
+ * Change the value of this variable to modify the location update rate
+ *
+ */
+ locationUpdateRate : 3000,
+
+ /**
+ *
+ * This variable represents if the current device is capable of running the Wikitude SDK
+ *
+ */
+ isDeviceSupported : false,
+
+ /**
+ *
+ * This watchID is used to shedule location updates
+ *
+ */
+ watchID : null,
+
+ /**
+ *
+ * Callbacks to get device information if ARchitect Worlds can be launched
+ *
+ */
+ onDeviceSupportedCallback : null,
+ onDeviceNotSupportedCallback : null,
+
+ /**
+ *
+ * Callbacks to get notified if the ARchitect World finished launching or if something went wrong during the World launch
+ *
+ */
+ onARchitectWorldLaunchedCallback : null,
+ onARchitectWorldFailedLaunchingCallback : null,
+
+ /**
+ *
+ * This function gets called if PhoneGap reports that it has finished loading successfully.
+ *
+ */
+ isDeviceSupported: function(successCallback, errorCallback)
+ {
+
+ WikitudePlugin.onDeviceSupportedCallback = successCallback;
+ WikitudePlugin.onDeviceNotSupportedCallback = errorCallback;
+
+
+ // PhoneGap is running, so the first thing we do is to check if the current device is capable of running the Wikitude Plugin
+ cordova.exec(WikitudePlugin.deviceIsARchitectReady, WikitudePlugin.deviceIsNotARchitectReady, "WikitudePlugin", "isDeviceSupported", [""]);
+
+ },
+
+ /**
+ *
+ * Declare what should happen when user pressed back-button while architect-view is opened.
+ * If not declared/called or null: ARchitect-View is closed
+ *
+ */
+ onPressedBackButton : function(callback)
+ {
+ WikitudePlugin.backButtonCallback = callback;
+ },
+
+ /**
+ *
+ * This function gets called if the Wikitude Plugin reports that the device is able to start the Wikitude SDK
+ *
+ */
+ deviceIsARchitectReady : function()
+ {
+ // We keep track of the device status
+ WikitudePlugin.isDeviceSupported = true;
+
+
+ if(WikitudePlugin.onDeviceSupportedCallback)
+ {
+ WikitudePlugin.onDeviceSupportedCallback();
+ }
+ },
+
+ /**
+ *
+ * This function gets called if the Wikitude Plugin reports that the device is not able of starting the Wikitude SDK.
+ *
+ */
+ deviceIsNotARchitectReady : function()
+ {
+ WikitudePlugin.isDeviceSupported = false;
+
+ // In this case we notify the user that his device is not supported by the Wikitude SDK
+ if(WikitudePlugin.onDeviceNotSupportedCallback)
+ {
+ WikitudePlugin.onDeviceNotSupportedCallback();
+ }
+ },
+
+ /*
+ * =============================================================================================================================
+ *
+ * PUBLIC API
+ *
+ * =============================================================================================================================
+ */
+
+ /* Managing ARchitect world loading */
+
+ /**
+ *
+ * Call this function if you want to load an ARchitect World
+ *
+ * @param {String} worldPath The path to an ARchitect world ether on the device or on e.g. your dropbox
+ *
+ */
+ loadARchitectWorld : function(worldPath)
+ {
+
+ // before we actually call load, we check again if the device is able to open the world
+ if(WikitudePlugin.isDeviceSupported)
+ {
+
+ // the 'open' function of the Wikitude Plugin requires a option dictionary with two keys:
+ // @param {Object} options (required)
+ // @param {String} options.sdkKey License key for the Wikitude SDK
+ // @param {String} options.filePath The path to a local ARchitect world or to a ARchitect world on a server or your dropbox
+
+ cordova.exec(WikitudePlugin.worldLaunched, WikitudePlugin.worldFailedLaunching, "WikitudePlugin", "open", [{ sdkKey: WikitudePlugin.mySDKKey, filePath: worldPath}]);
+
+
+ // We add an event listener on the resume and pause event of the application lifecycle
+ document.addEventListener("resume", WikitudePlugin.onResume, false);
+ document.addEventListener("pause", WikitudePlugin.onPause, false);
+
+ WikitudePlugin.isOpened = true;
+ document.addEventListener("backbutton", WikitudePlugin.onBackButton, false);
+
+ // After we started loading the world, we start location updates
+ WikitudePlugin.startLocationUpdates();
+
+ }else
+ {
+ // if the device is not able to start the Wikitude SDK, we notify the user again
+ WikitudePlugin.deviceNotARchitectReady();
+ }
+ },
+
+ /* Managing the Wikitude SDK Lifecycle */
+
+ /**
+ *
+ * Use this function to stop the Wikitude SDK and to remove the ARchitectView from the screen
+ *
+ */
+ close : function()
+ {
+ document.removeEventListener("pause", WikitudePlugin.onPause, false);
+ document.removeEventListener("resume", WikitudePlugin.onResume, false);
+
+ WikitudePlugin.stopLocationUpdates();
+
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "close", [""]);
+ WikitudePlugin.isOpened = false;
+ },
+
+ /**
+ *
+ * Use this function to only hide the Wikitude SDK. All location and rendering updates are still active
+ *
+ */
+ hide : function()
+ {
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "hide", [""]);
+ },
+
+ /**
+ *
+ * Use this function to show the Wikitude SDK if it was hidden before
+ *
+ */
+ show : function()
+ {
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "show", [""]);
+ },
+
+ /* Interacting with the Wikitude SDK */
+
+ /**
+ *
+ * Use this function to call javascript which will be executed in the context of your ARchitect World
+ *
+ *
+ * @param js The JavaScript that gets evaluated in context of the ARchitect World
+ *
+ */
+ callJavaScript : function(js)
+ {
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "callJavascript", [js]);
+ },
+
+ /**
+ *
+ * Use this function to set a callback which will be invoked when the ARchitect World calls for example
+ * document.location = "architectsdk://opendetailpage?id=9";
+ *
+ *
+ * @param onUrlInvokeCallback A function which gets called when the ARchitect World invokes a call to "document.location = architectsdk://"
+ */
+ setOnUrlInvokeCallback : function(onUrlInvokeCallback)
+ {
+ cordova.exec(onUrlInvokeCallback, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onUrlInvoke", [""]);
+ },
+
+
+ /*
+ * =============================================================================================================================
+ *
+ * Callbacks of public functions
+ *
+ * =============================================================================================================================
+ */
+
+ /**
+ *
+ * Use this callback to get notified if the world loaded successfully
+ *
+ */
+ worldLaunched : function()
+ {
+ if(WikitudePlugin.onARchitectWorldLaunchedCallback)
+ {
+ WikitudePlugin.onARchitectWorldLaunchedCallback();
+ }
+ },
+
+ /**
+ *
+ * Use this callback to get notified if the Wikitude SDK wasn't able to load the ARchitect World
+ *
+ */
+ worldFailedLaunching : function(err)
+ {
+ if(WikitudePlugin.onARchitectWorldFailedLaunchingCallback)
+ {
+ WikitudePlugin.onARchitectWorldFailedLaunchingCallback(err);
+ }
+ },
+
+ /* Lifecycle updates */
+
+ /**
+ *
+ * This function actually starts the PhoneGap location updates
+ *
+ */
+ startLocationUpdates : function()
+ {
+
+ WikitudePlugin.watchID = navigator.geolocation.watchPosition(WikitudePlugin.onReceivedLocation, WikitudePlugin.onWikitudeError, { frequency: WikitudePlugin.locationUpdateRate });
+ },
+
+ /**
+ *
+ * This callback gets called everytime the location did update
+ *
+ */
+ onReceivedLocation : function(position)
+ {
+
+ // Every time that PhoneGap did received a location update, we pass the location into the Wikitude SDK
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "setLocation", [{ lat: position.coords.latitude, lon: position.coords.longitude, alt: position.coords.altitude, acc: position.coords.accuracy}]);
+ },
+
+ /**
+ *
+ * Use this function to stop location updates
+ *
+ */
+ stopLocationUpdates : function()
+ {
+
+ // We clear the location update watch which was responsible for updating the location in a specific time interval
+ navigator.geolocation.clearWatch(WikitudePlugin.watchID);
+ WikitudePlugin.watchID = null;
+ },
+
+ /**
+ *
+ * This function gets called every time the application did become active.
+ *
+ */
+ onResume : function()
+ {
+
+ // Call the Wikitude SDK that the application did become active again
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onResume", [""]);
+
+ // And start continuing updating the user location
+ WikitudePlugin.startLocationUpdates();
+ },
+
+ /**
+ *
+ * This function gets called every time the application is about to become inactive
+ *
+ */
+ onPause : function()
+ {
+
+ // Call the Wikitude SDK that the application did resign active
+ cordova.exec(WikitudePlugin.onWikitudeOK, WikitudePlugin.onWikitudeError, "WikitudePlugin", "onPause", [""]);
+
+ // And stop all ongoing location updates
+ WikitudePlugin.stopLocationUpdates();
+ },
+
+ /**
+ *
+ * Android specific!
+ * This function gets called if the user presses the back button.
+ * You may define your own implementation by using WikitudePlugin.onPressedBackButton(method)
+ *
+ */
+ onBackButton : function()
+ {
+ if (WikitudePlugin.isOpened) {
+ if (WikitudePlugin.backButtonCallback==null) {
+ WikitudePlugin.close();
+ document.removeEventListener("backbutton", WikitudePlugin.onBackButton, false);
+ } else {
+ WikitudePlugin.backButtonCallback();
+ }
+ }
+ },
+
+ /**
+ *
+ * This function gets called every time the application is about to become inactive
+ *
+ */
+ onWikitudeOK : function()
+ {
+ },
+
+ /**
+ *
+ * This function gets called every time the application is about to become inactive
+ *
+ */
+ onWikitudeError : function()
+ {
+ },
+
+ /**
+ *
+ * This function gets called every time the application is about to become inactive
+ *
+ */
+ report: function(id)
+ {
+ console.log("app report: " + id);
+ }
+};
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/js/index.js b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/js/index.js
new file mode 100644
index 0000000..b82a058
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/js/index.js
@@ -0,0 +1,37 @@
+var app = {
+ initialize: function() {
+ this.bind();
+ },
+ bind: function() {
+ document.addEventListener('deviceready', this.deviceready, false);
+ },
+
+ // A callback which gets called if the device is able to launch ARchitect Worlds
+ onDeviceSupportedCallback : function()
+ {
+ // The device is able to launch ARchitect World, so lets do so :)
+ WikitudePlugin.loadARchitectWorld("assets/world/SimpleImageRecognition/SimpleIRWorld.html");
+ },
+
+ // A callback which gets called if the device is not able to start ARchitect Worlds
+ onDeviceNotSupportedCallback : function()
+ {
+ app.report("Unable to launch ARchitect Worlds on this device");
+ },
+
+ deviceready: function() {
+ // note that this is an event handler so the scope is that of the event
+ // so we need to call app.report(), and not this.report()
+ app.report('deviceready');
+
+ // check if the current device is able to launch ARchitect Worlds
+ WikitudePlugin.isDeviceSupported(app.onDeviceSupportedCallback, app.onDeviceNotSupportedCallback);
+ },
+ report: function(id) {
+ console.log("report:" + id);
+ // hide the .pending
and show the .complete
+ document.querySelector('#' + id + ' .pending').className += ' hide';
+ var completeElem = document.querySelector('#' + id + ' .complete');
+ completeElem.className = completeElem.className.split('hide').join('');
+ }
+};
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_128.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_128.png
new file mode 100644
index 0000000..3516df3
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_128.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_16.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_16.png
new file mode 100644
index 0000000..54e19c5
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_16.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_24.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_24.png
new file mode 100644
index 0000000..c7d43ad
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_24.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_256.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_256.png
new file mode 100644
index 0000000..e1cd0e6
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_256.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_32.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_32.png
new file mode 100644
index 0000000..734fffc
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_32.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_48.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_48.png
new file mode 100644
index 0000000..8ad8bac
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_48.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_512.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_512.png
new file mode 100644
index 0000000..c9465f3
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_512.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_64.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_64.png
new file mode 100644
index 0000000..03b3849
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_64.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_36.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_36.png
new file mode 100644
index 0000000..cd5032a
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_36.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_48.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_48.png
new file mode 100644
index 0000000..e79c606
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_48.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_72.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_72.png
new file mode 100644
index 0000000..4d27634
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_72.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_96.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_96.png
new file mode 100644
index 0000000..ec7ffbf
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_android_96.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_bb_80.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_bb_80.png
new file mode 100644
index 0000000..f86a27a
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_bb_80.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_114.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_114.png
new file mode 100644
index 0000000..efd9c37
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_114.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_144.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_144.png
new file mode 100644
index 0000000..dd819da
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_144.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_57.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_57.png
new file mode 100644
index 0000000..c795fc4
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_57.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_72.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_72.png
new file mode 100644
index 0000000..b1cfde7
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/icon/cordova_ios_72.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_hdpi_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_hdpi_landscape.png
new file mode 100644
index 0000000..a61e2b1
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_hdpi_landscape.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_hdpi_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_hdpi_portrait.png
new file mode 100644
index 0000000..5d6a28a
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_hdpi_portrait.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_ldpi_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_ldpi_landscape.png
new file mode 100644
index 0000000..f3934cd
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_ldpi_landscape.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_ldpi_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_ldpi_portrait.png
new file mode 100644
index 0000000..65ad163
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_ldpi_portrait.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_mdpi_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_mdpi_landscape.png
new file mode 100644
index 0000000..a1b697c
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_mdpi_landscape.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_mdpi_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_mdpi_portrait.png
new file mode 100644
index 0000000..ea15693
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_mdpi_portrait.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_xhdpi_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_xhdpi_landscape.png
new file mode 100644
index 0000000..79f2f09
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_xhdpi_landscape.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_xhdpi_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_xhdpi_portrait.png
new file mode 100644
index 0000000..c2e8042
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/android_xhdpi_portrait.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/blackberry_transparent_300.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/blackberry_transparent_300.png
new file mode 100644
index 0000000..b548bdc
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/blackberry_transparent_300.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/blackberry_transparent_400.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/blackberry_transparent_400.png
new file mode 100644
index 0000000..3facdf9
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/blackberry_transparent_400.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_landscape.png
new file mode 100644
index 0000000..04be5ac
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_landscape.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_portrait.png
new file mode 100644
index 0000000..41e839d
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_portrait.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_retina_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_retina_landscape.png
new file mode 100644
index 0000000..95c542d
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_retina_landscape.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_retina_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_retina_portrait.png
new file mode 100644
index 0000000..aae1862
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/ipad_retina_portrait.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_landscape.png
new file mode 100644
index 0000000..d154883
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_landscape.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_portrait.png
new file mode 100644
index 0000000..6fcba56
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_portrait.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_retina_landscape.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_retina_landscape.png
new file mode 100644
index 0000000..0165669
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_retina_landscape.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_retina_portrait.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_retina_portrait.png
new file mode 100644
index 0000000..bd24886
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/iphone_retina_portrait.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/windows_phone_portrait.jpg b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/windows_phone_portrait.jpg
new file mode 100644
index 0000000..9f95387
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/res/screen/windows_phone_portrait.jpg differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/spec.html b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/spec.html
new file mode 100644
index 0000000..83d7d2e
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/assets/www/spec.html
@@ -0,0 +1,50 @@
+
+
+
'].join('\n');
+ });
+
+ it('should show the completion state', function() {
+ app.report('deviceready');
+ var el = document.querySelector('#deviceready .complete:not(.hide)');
+ expect(el).toBeTruthy();
+ });
+
+ it('should hide the pending state', function() {
+ app.report('deviceready');
+ var el = document.querySelector('#deviceready .pending.hide');
+ expect(el).toBeTruthy();
+ });
+ });
+});
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/BOOM b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/BOOM
new file mode 100644
index 0000000..37c623c
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/BOOM
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -e
+
+CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
+
+bash $CORDOVA_PATH/cordova BOOM
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/appinfo.jar b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/appinfo.jar
new file mode 100644
index 0000000..37e00df
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/appinfo.jar differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/clean b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/clean
new file mode 100644
index 0000000..daa8442
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/clean
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -e
+
+CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
+
+bash $CORDOVA_PATH/cordova clean
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/cordova b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/cordova
new file mode 100644
index 0000000..0bca03f
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/cordova
@@ -0,0 +1,85 @@
+#!/bin/bash
+
+set -e
+
+PROJECT_PATH=$( cd "$( dirname "$0" )/.." && pwd )
+
+function check_devices {
+ local devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}'`
+ if [ -z "$devices" ] ; then
+ echo "1"
+ else
+ echo "0"
+ fi
+}
+
+function emulate {
+ declare -a avd_list=($(android list avd | grep "Name:" | cut -f 2 -d ":" | xargs))
+ # we need to start adb-server
+ adb start-server 1>/dev/null
+
+ # Do not launch an emulator if there is already one running or if a device is attached
+ if [ $(check_devices) == 0 ] ; then
+ echo "Device attached or emulator already running"
+ return
+ fi
+
+ local avd_id="1000" #FIXME: hopefully user does not have 1000 AVDs
+ # User has no AVDs
+ if [ ${#avd_list[@]} == 0 ]
+ then
+ echo "You don't have any Android Virtual Devices. Please create at least one AVD."
+ echo "android"
+ fi
+ # User has only one AVD
+ if [ ${#avd_list[@]} == 1 ]
+ then
+ emulator -cpu-delay 0 -no-boot-anim -cache /tmp/cache -avd ${avd_list[0]} 1> /dev/null 2>&1 &
+ # User has more than 1 AVD
+ elif [ ${#avd_list[@]} -gt 1 ]
+ then
+ while [ -z ${avd_list[$avd_id]} ]
+ do
+ echo "Choose from one of the following Android Virtual Devices [0 to $((${#avd_list[@]}-1))]:"
+ for(( i = 0 ; i < ${#avd_list[@]} ; i++ ))
+ do
+ echo "$i) ${avd_list[$i]}"
+ done
+ echo -n "> "
+ read avd_id
+ done
+ emulator -cpu-delay 0 -no-boot-anim -cache /tmp/cache -avd ${avd_list[$avd_id]} 1> /dev/null 2>&1 &
+ fi
+
+}
+
+function clean {
+ ant clean
+}
+# has to be used independently and not in conjuction with other commands
+function log {
+ adb logcat
+}
+
+function debug {
+ if [ $(check_devices) == 0 ] ; then
+ ant debug install
+ else
+ ant debug
+ echo "##################################################################"
+ echo "# Plug in your device or launch an emulator with cordova/emulate #"
+ echo "##################################################################"
+ fi
+}
+
+function launch {
+ local launch_str=$(java -jar $PROJECT_PATH/cordova/appinfo.jar $PROJECT_PATH/AndroidManifest.xml)
+ adb shell am start -n $launch_str
+}
+
+function BOOM {
+ clean && debug && launch
+}
+
+# TODO parse arguments
+(cd $PROJECT_PATH && $1)
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/debug b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/debug
new file mode 100644
index 0000000..5d63a39
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/debug
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -e
+
+CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
+
+bash $CORDOVA_PATH/cordova debug
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/emulate b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/emulate
new file mode 100644
index 0000000..6c4fab2
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/emulate
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -e
+
+CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
+
+bash $CORDOVA_PATH/cordova emulate
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/log b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/log
new file mode 100644
index 0000000..ab3622e
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/cordova/log
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -e
+
+PROJECT_PATH=$( cd "$( dirname "$0" )/.." && pwd )
+
+bash $PROJECT_PATH/cordova/cordova log
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/libs/cordova-2.0.0.jar b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/libs/cordova-2.0.0.jar
new file mode 100644
index 0000000..e01123b
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/libs/cordova-2.0.0.jar differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/proguard-project.txt b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/project.properties b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/project.properties
new file mode 100644
index 0000000..0840b4a
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-15
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-hdpi/ic_launcher.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-hdpi/ic_launcher.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-ldpi/ic_launcher.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..9923872
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-ldpi/ic_launcher.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-mdpi/ic_launcher.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-mdpi/ic_launcher.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-xhdpi/ic_launcher.png b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
Binary files /dev/null and b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/layout/main.xml b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/layout/main.xml
new file mode 100644
index 0000000..991ac56
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/layout/main.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/values/strings.xml b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/values/strings.xml
new file mode 100644
index 0000000..3c5860a
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+
+
+ Hello World, PhonegapSampleActivity!
+ Hello IR
+
+
\ No newline at end of file
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/xml/config.xml b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/xml/config.xml
new file mode 100644
index 0000000..4682e93
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/res/xml/config.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/src/com/wikitude/phonegap/PhonegapSampleActivity.java b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/src/com/wikitude/phonegap/PhonegapSampleActivity.java
new file mode 100644
index 0000000..435e9e4
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/src/com/wikitude/phonegap/PhonegapSampleActivity.java
@@ -0,0 +1,17 @@
+package com.wikitude.phonegap;
+
+import org.apache.cordova.DroidGap;
+
+import android.os.Bundle;
+
+
+
+public class PhonegapSampleActivity extends DroidGap {
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate( Bundle savedInstanceState ) {
+ super.onCreate( savedInstanceState );
+ super.loadUrl( "file:///android_asset/www/index.html" );
+ // super.loadUrl( "file:///android_asset/www/demo.html" );
+ }
+}
\ No newline at end of file
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/src/com/wikitude/phonegap/WikitudePlugin.java b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/src/com/wikitude/phonegap/WikitudePlugin.java
new file mode 100644
index 0000000..f88af1d
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/src/com/wikitude/phonegap/WikitudePlugin.java
@@ -0,0 +1,417 @@
+package com.wikitude.phonegap;
+
+import java.io.IOException;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.app.Activity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.ViewManager;
+
+import com.phonegap.api.Plugin;
+import com.phonegap.api.PluginResult;
+import com.wikitude.architect.ArchitectUrlListener;
+import com.wikitude.architect.ArchitectView;
+import com.wikitude.architect.ArchitectView.ArchitectConfig;
+
+
+
+/**
+ * Basic PhoneGap Wikitude ARchitect Plugin
+ *
+ * @author Wikitude GmbH
+ */
+public class WikitudePlugin extends Plugin implements ArchitectUrlListener {
+
+ /** PhoneGap-root to Android-App-assets folder */
+ private static final String LOCAL_ASSETS_PATH_ROOT = "assets/";
+
+ /* various JSON-Object keys*/
+ private static final String JSON_KEY_APIKEY = "sdkKey";
+ private static final String JSON_KEY_FILE_PATH = "filePath";
+
+ private static final String JSON_KEY_LOCATION_ALTITUDE = "alt";
+ private static final String JSON_KEY_LOCATION_ACCURACY = "acc";
+ private static final String JSON_KEY_LOCATION_LATITUDE = "lat";
+ private static final String JSON_KEY_LOCATION_LONGITUDE = "lon";
+
+ /* static action strings */
+
+ /**
+ * opens architect-view (add to view stack)
+ */
+ private static final String ACTION_OPEN = "open";
+
+ /**
+ * closes architect-view (remove view stack)
+ */
+ private static final String ACTION_CLOSE = "close";
+
+ /**
+ * set visibility of architectView to visible (of present)
+ */
+ private static final String ACTION_SHOW = "show";
+
+ /**
+ * set visibility of architectView to invisible (of present)
+ */
+ private static final String ACTION_HIDE = "hide";
+
+ /**
+ * inject location information
+ */
+ private static final String ACTION_SET_LOCATION = "setLocation";
+
+ /**
+ * callback for uri-invocations
+ */
+ private static final String ACTION_ON_URLINVOKE = "onUrlInvoke";
+
+ /**
+ * life-cycle notification for resume
+ */
+ private static final String ACTION_ON_RESUME = "onResume";
+
+ /**
+ * life-cycle notification for pause
+ */
+ private static final String ACTION_ON_PAUSE = "onPause";
+
+ /**
+ * check if view is on view-stack (no matter if visible or not)
+ */
+ private static final String ACTION_STATE_ISOPEN = "isOpen";
+
+ /**
+ * opens architect-view (add to view stack)
+ */
+ private static final String ACTION_IS_DEVICE_SUPPORTED = "isDeviceSupported";
+
+ /**
+ * check if view is on view-stack (no matter if visible or not)
+ */
+ private static final String ACTION_CALL_JAVASCRIPT = "callJavascript";
+
+ /**
+ * the Wikitude ARchitectview
+ */
+ private ArchitectView architectView;
+
+ /**
+ * callback-Id of url-invocation method
+ */
+ private String urlInvokeCallbackId = null;
+
+ /**
+ * callback-id of "open"-action method
+ */
+ private String openCallbackId = null;
+
+ @Override
+ public PluginResult execute( final String action, final JSONArray args, final String callbackId ) {
+
+ /* hide architect-view -> destroy and remove from activity */
+ if ( WikitudePlugin.ACTION_CLOSE.equals( action ) ) {
+ if ( this.architectView != null ) {
+ this.cordova.getActivity().runOnUiThread( new Runnable() {
+
+ @Override
+ public void run() {
+ removeArchitectView();
+ }
+ } );
+ return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" );
+ }
+ else {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" );
+ }
+ }
+
+ /* return success only if view is opened (no matter if visible or not) */
+ if ( WikitudePlugin.ACTION_STATE_ISOPEN.equals( action ) ) {
+ if ( this.architectView != null ) {
+ return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" );
+ } else {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" );
+ }
+ }
+
+ /* return success only if view is opened (no matter if visible or not) */
+ if ( WikitudePlugin.ACTION_IS_DEVICE_SUPPORTED.equals( action ) ) {
+ if ( ArchitectView.isDeviceSupported( this.cordova.getActivity() ) ) {
+ return new PluginResult( PluginResult.Status.OK, action + ": this device is ARchitect-ready" );
+ } else {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": Sorry, this device is NOT ARchitect-ready" );
+ }
+ }
+
+
+
+ /* life-cycle's RESUME */
+ if ( WikitudePlugin.ACTION_ON_RESUME.equals( action ) ) {
+
+ if ( this.architectView != null ) {
+ this.cordova.getActivity().runOnUiThread( new Runnable() {
+
+ @Override
+ public void run() {
+ WikitudePlugin.this.architectView.onResume();
+ }
+ } );
+
+ return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" );
+ }
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" );
+ }
+
+ /* life-cycle's PAUSE */
+ if ( WikitudePlugin.ACTION_ON_PAUSE.equals( action ) ) {
+ if ( architectView != null ) {
+ this.cordova.getActivity().runOnUiThread( new Runnable() {
+
+ @Override
+ public void run() {
+ WikitudePlugin.this.architectView.onPause();
+ }
+ } );
+
+ return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" );
+ }
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" );
+ }
+
+ /* set visibility to "visible", return error if view is null */
+ if ( WikitudePlugin.ACTION_SHOW.equals( action ) ) {
+
+ if ( this.architectView != null ) {
+ this.architectView.setVisibility( View.VISIBLE );
+ return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" );
+ } else {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" );
+ }
+ }
+
+ /* set visibility to "invisible", return error if view is null */
+ if ( WikitudePlugin.ACTION_HIDE.equals( action ) ) {
+
+ if ( this.architectView != null ) {
+ this.architectView.setVisibility( View.INVISIBLE );
+ return new PluginResult( PluginResult.Status.OK, action + ": architectView is present" );
+ } else {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectView is not present" );
+ }
+ }
+
+ /* define call-back for url-invocations */
+ if ( WikitudePlugin.ACTION_ON_URLINVOKE.equals( action ) ) {
+ this.urlInvokeCallbackId = callbackId;
+ PluginResult result = new PluginResult( PluginResult.Status.NO_RESULT, action + ": registered callback" );
+ result.setKeepCallback( true );
+ return result;
+ }
+
+ /* location update */
+ if ( WikitudePlugin.ACTION_SET_LOCATION.equals( action ) ) {
+ if ( this.architectView != null ) {
+ try {
+ String arrStr = args.getString( 0 );
+ JSONObject arr = new JSONObject( arrStr );
+ final double lat = arr.getDouble( WikitudePlugin.JSON_KEY_LOCATION_LATITUDE );
+ final double lon = arr.getDouble( WikitudePlugin.JSON_KEY_LOCATION_LONGITUDE );
+ Object altObj = arr.get( WikitudePlugin.JSON_KEY_LOCATION_ALTITUDE );
+ float alt = Float.MIN_VALUE;
+
+ if ( altObj != null && altObj instanceof Double ) {
+ alt = ((Double)altObj).floatValue();
+ }
+
+ final float altitude = alt;
+
+ final Double acc = arr.getDouble( WikitudePlugin.JSON_KEY_LOCATION_ACCURACY );
+ if ( this.cordova != null && this.cordova.getActivity() != null ) {
+ this.cordova.getActivity().runOnUiThread( new Runnable() {
+
+ @Override
+ public void run() {
+ if ( acc != null ) {
+ WikitudePlugin.this.architectView.setLocation( lat, lon, altitude, acc.floatValue() );
+ } else {
+ WikitudePlugin.this.architectView.setLocation( lat, lon, altitude );
+ }
+ }
+ } );
+ }
+
+ } catch ( Exception e ) {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": exception thrown, " + e != null ? e.getMessage() : "(exception is NULL)" );
+ }
+ return new PluginResult( PluginResult.Status.OK, action + ": updated location" );
+ }
+
+ /* return error if there is no architect-view active*/
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectview is not active" );
+ }
+
+ if ( WikitudePlugin.ACTION_CALL_JAVASCRIPT.equals( action ) ) {
+ if ( this.architectView != null ) {
+ String logMsg = null;
+ try {
+ final String callJS = args.getString( 0 );
+ logMsg = callJS;
+ this.cordova.getActivity().runOnUiThread( new Runnable() {
+
+ @Override
+ public void run() {
+ WikitudePlugin.this.architectView.callJavascript( callJS );
+ }
+ } );
+
+ } catch ( JSONException je ) {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": exception thrown, " + je != null ? je.getMessage() : "(exception is NULL)" );
+ }
+ return new PluginResult( PluginResult.Status.OK, action + ": called js, '" + logMsg + "'" );
+ } else {
+ return new PluginResult( PluginResult.Status.ERROR, action + ": architectview is not active" );
+ }
+ }
+
+
+ /* initial set-up, show ArchitectView full-screen in current screen/activity */
+ if ( WikitudePlugin.ACTION_OPEN.equals( action ) ) {
+ this.openCallbackId = callbackId;
+ PluginResult result = null;
+ String arrStr = null;
+
+ try {
+ arrStr = args.getString( 0 );
+
+ // arrStr = arrStr.substring( 1, arrStr.length() - 1 );
+
+ JSONObject arr = new JSONObject( arrStr );
+
+ final String apiKey = arr.getString( WikitudePlugin.JSON_KEY_APIKEY );
+ final String filePath = arr.getString( WikitudePlugin.JSON_KEY_FILE_PATH );
+
+ this.cordova.getActivity().runOnUiThread( new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ WikitudePlugin.this.addArchitectView( apiKey, filePath );
+
+ /* call success method once architectView was added successfully */
+ if ( WikitudePlugin.this.openCallbackId != null ) {
+ PluginResult result = new PluginResult( PluginResult.Status.OK );
+ result.setKeepCallback( false );
+ WikitudePlugin.this.success( result, WikitudePlugin.this.openCallbackId );
+ }
+ } catch ( Exception e ) {
+ /* in case "addArchitectView" threw an exception -> notify callback method asynchronously */
+ WikitudePlugin.this.error( e != null ? e.getMessage() : "Exception is 'null'", WikitudePlugin.this.openCallbackId );
+ }
+ }
+ } );
+
+ } catch ( Exception e ) {
+ result = new PluginResult( PluginResult.Status.ERROR, action + ": exception thown, " + e != null ? e.getMessage() : "(exception is NULL)" );
+ result.setKeepCallback( false );
+ return result;
+ }
+
+ /* adding architect-view is done in separate thread, ensure to setKeepCallback so one can call success-method properly later on */
+ result = new PluginResult( PluginResult.Status.NO_RESULT, action + ": no result required, just registered callback-method" );
+ result.setKeepCallback( true );
+
+ return result;
+ }
+
+ /* fall-back return value */
+ return new PluginResult( PluginResult.Status.ERROR, "no such action: " + action );
+ }
+
+ /**
+ * called when url was invoked in architectView (by e.g. calling document.location = "myprotocoll://foo";
+ * @param url the invoked url (e.g. "myprotocoll://foo")
+ * @return true if call was handled properly
+ */
+ @Override
+ public boolean urlWasInvoked( String url ) {
+
+ /* call callback-method if set*/
+ if ( this.urlInvokeCallbackId != null ) {
+ try {
+ /* pass called url as String to callback-method */
+ PluginResult res = new PluginResult( PluginResult.Status.OK, url );
+ res.setKeepCallback( true );
+ this.success( res, this.urlInvokeCallbackId );
+ return true;
+ } catch ( Exception e ) {
+ this.error( "invalid url invoked: " + url, this.urlInvokeCallbackId );
+ }
+ }
+ return false;
+ }
+
+ /**
+ * hides/removes ARchitect-View completely
+ * @return true if successful, false otherwise
+ */
+ private boolean removeArchitectView() {
+ if ( this.architectView != null ) {
+ /* fake life-cycle calls, because activity is already up and running */
+ this.architectView.onPause();
+ this.architectView.onDestroy();
+ this.architectView.setVisibility( View.INVISIBLE );
+ ((ViewManager)this.architectView.getParent()).removeView( this.architectView );
+ this.architectView = null;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Architect-Configuration required for proper set-up
+ * @param apiKey
+ * @return
+ */
+ protected ArchitectConfig getArchitectConfig( final String apiKey ) {
+ /* no special set-up required in default Wikitude-Plugin, further things required in advanced usage (e.g. Vuforia Image Recognition) */
+ return new ArchitectConfig( apiKey );
+ }
+
+ /**
+ * add architectView to current screen
+ * @param apiKey developers's api key to use (hides watermarking/intro-animation if it matches your package-name)
+ * @param filePath the url (starting with http:// for online use; starting with LOCAL_ASSETS_PATH_ROOT if oyu want to load assets within your app-assets folder)
+ * @throws IOException might be thrown from ARchitect-SDK
+ */
+ @SuppressWarnings("deprecation")
+ private void addArchitectView( final String apiKey, String filePath ) throws IOException {
+ if ( this.architectView == null ) {
+ this.architectView = new ArchitectView( (Activity)this.ctx.getContext() );
+
+ /* add content view and fake initial life-cycle */
+ ((Activity)this.ctx.getContext()).addContentView( this.architectView, new ViewGroup.LayoutParams( LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT ) );
+
+ /* fake life-cycle calls, because activity is already up and running */
+ this.architectView.onCreate( getArchitectConfig( apiKey ) );
+ this.architectView.onPostCreate();
+
+ /* register self as url listener to fwd these native calls to PhoneGap */
+ this.architectView.registerUrlListener( WikitudePlugin.this );
+
+ /* load asset from local directory if prefix is used */
+ if ( filePath.startsWith( WikitudePlugin.LOCAL_ASSETS_PATH_ROOT ) ) {
+ filePath = filePath.substring( WikitudePlugin.LOCAL_ASSETS_PATH_ROOT.length() );
+ }
+ this.architectView.load( filePath );
+
+ /* also a fake-life-cycle call (the last one before it is really shown in UI */
+ this.architectView.onResume();
+ }
+ }
+}
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/src/com/wikitude/phonegap/WikitudePluginExtended.java b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/src/com/wikitude/phonegap/WikitudePluginExtended.java
new file mode 100644
index 0000000..fb62e58
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/HelloImageRecognition/src/com/wikitude/phonegap/WikitudePluginExtended.java
@@ -0,0 +1,89 @@
+package com.wikitude.phonegap;
+
+import android.app.Activity;
+
+import com.qualcomm.QCAR.QCAR;
+import com.wikitude.architect.ArchitectView.ArchitectConfig;
+import com.wikitude.architect.VuforiaInterface;
+
+
+
+/**
+ * Advanced Wikitude ARchitect Plugin (= Basic Plugin + Vuforia Image Recognition feature-set)
+ *
+ * You must add "wikitudesdk.jar" and "QCAR.lib" to your libs folder and build-path and have "libQCAR.so" in project's "libs/armeabi/" directory
+ *
+ * Also add ""
+ * (replace Basic-plugin entry if necessary)
+ *
+ * in config.xml to enable this plug-in in your project; Ensure your architectSDK key is Vuforia-ready
+ *
+ * Note:
+ * This plug-in is written under Apache License, Version 2.0
+ * http://www.apache.org/licenses/LICENSE-2.0.html
+ *
+ * @version 1.0.0
+ * @author Wikitude GmbH; www.wikitude.com
+ */
+public class WikitudePluginExtended extends WikitudePlugin {
+
+
+ static {
+ System.loadLibrary( "QCAR" );
+ }
+
+ /**
+ * Architect-Configuration required for proper set-up
+ * @param apiKey
+ * @return
+ */
+ @Override
+ protected ArchitectConfig getArchitectConfig( final String apiKey ) {
+ final ArchitectConfig config = super.getArchitectConfig( apiKey );
+
+ /* required for Vuforia */
+ config.setVuforiaInterface( new VuforiaServiceImplementation() );
+ return config;
+ }
+
+ /* required for Vuforia */
+ private class VuforiaServiceImplementation implements VuforiaInterface {
+
+ @Override
+ public void deInit() {
+ QCAR.deinit();
+ }
+
+ @Override
+ public int init() {
+ return QCAR.init();
+ }
+
+ @Override
+ public void onPause() {
+ QCAR.onPause();
+ }
+
+ @Override
+ public void onResume() {
+ QCAR.onResume();
+ }
+
+ @Override
+ public void onSurfaceChanged( final int arg0, final int arg1 ) {
+ QCAR.onSurfaceChanged( arg0, arg1 );
+ }
+
+ @Override
+ public void onSurfaceCreated() {
+ QCAR.onSurfaceCreated();
+ }
+
+ @Override
+ public void setInitParameters( final Activity activity, final int nFlags ) {
+ QCAR.setInitParameters( activity, nFlags );
+ }
+
+ }
+
+}
diff --git a/Android/AugmentedReality-Wikitude/SampleProjects/Extended/README.md b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/README.md
new file mode 100644
index 0000000..be5b0d5
--- /dev/null
+++ b/Android/AugmentedReality-Wikitude/SampleProjects/Extended/README.md
@@ -0,0 +1,22 @@
+
+# Hello Image Recognition Android
+
+This document describes all necessary steps to get the sample running.
+Once set up, the app augments the [Wikitude Logo](http://www.flickr.com/photos/wikitude/5571196416/) in your camera.
+
+
+####Prerequisites:
+* You need to [register as developer at Wikitude](http://developer.wikitude.com) and downloaded the SDK, two files from the SDK's Android-folder are required, as described below.
+
+* Download the Vuforia Framework from [Qualcomm](https://ar.qualcomm.at/sdk/android) (You need to register yourself as a Qualcomm developer)
+
+
+# SETUP
+
+
+* [Import HelloImageRecognition-project in Eclipse as Android-Project](https://www.google.com/webhp?sourceid=chrome-instant&ie=UTF-8&ion=1#hl=de&output=search&sclient=psy-ab&q=import%20android%20project%20into%20eclipse&oq=&gs_l=&pbx=1&fp=531bf0abdc9ea0e7&ion=1&bav=on.2,or.r_gc.r_pw.r_cp.r_qf.&biw=1030&bih=550)
+* Copy `wikitudesdk.jar` from the Android [WikitudeSDK](http://developer.wikitude.com) to project's `libs`-folder
+
+* Copy `libExtensionVuforia.so` from the Android [WikitudeSDK](http://developer.wikitude.com) to `libs/armeabi`-folder (create subfolder if necessary)
+
+* From [Qualcomm's Vuforia SDK](https://ar.qualcomm.at/sdk/android): Copy `QCAR.jar` to your projects `libs`-folder and add it to your project's build. Also copy Vuforia's `libQCAR.so` to your `libs/armeabi`-folder
\ No newline at end of file
diff --git a/Android/BackgroundService/1.8.1/MyService.java b/Android/BackgroundService/1.8.1/MyService.java
new file mode 100644
index 0000000..cb39447
--- /dev/null
+++ b/Android/BackgroundService/1.8.1/MyService.java
@@ -0,0 +1,78 @@
+package com.yournamespace.yourappname;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.util.Log;
+
+import com.red_folder.phonegap.plugin.backgroundservice.BackgroundService;
+
+public class MyService extends BackgroundService {
+
+ private final static String TAG = MyService.class.getSimpleName();
+
+ private String mHelloTo = "World";
+
+ @Override
+ protected JSONObject doWork() {
+ JSONObject result = new JSONObject();
+
+ try {
+ SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
+ String now = df.format(new Date(System.currentTimeMillis()));
+
+ String msg = "Hello " + this.mHelloTo + " - its currently " + now;
+ result.put("Message", msg);
+
+ Log.d(TAG, msg);
+ } catch (JSONException e) {
+ }
+
+ return result;
+ }
+
+ @Override
+ protected JSONObject getConfig() {
+ JSONObject result = new JSONObject();
+
+ try {
+ result.put("HelloTo", this.mHelloTo);
+ } catch (JSONException e) {
+ }
+
+ return result;
+ }
+
+ @Override
+ protected void setConfig(JSONObject config) {
+ try {
+ if (config.has("HelloTo"))
+ this.mHelloTo = config.getString("HelloTo");
+ } catch (JSONException e) {
+ }
+
+ }
+
+ @Override
+ protected JSONObject initialiseLatestResult() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected void onTimerEnabled() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ protected void onTimerDisabled() {
+ // TODO Auto-generated method stub
+
+ }
+
+
+}
diff --git a/Android/BackgroundService/1.8.1/README.md b/Android/BackgroundService/1.8.1/README.md
new file mode 100644
index 0000000..58ac0aa
--- /dev/null
+++ b/Android/BackgroundService/1.8.1/README.md
@@ -0,0 +1,69 @@
+# Background Service Plugin for Phonegap #
+
+A plugin (and framework code) that allows the development and operation of an Android Background Service.
+
+The example MyService Background Service will write a Hello message to the LogCat every minute. The MyService is designed as sample code.
+
+## Adding the plugin to your project ##
+
+Copy the files to the following locations:
+
+* libs\backgroundserviceplugin.jar
+* src\com\yournamespace\yourappname\MyService.java
+* assets\www\backgroundService.js
+* assets\www\myService.js
+* assets\www\index.html
+
+Add the following to res\xml\plugins.xml
+
+```
+
+```
+
+Add the following to AndroidManifest.xml
+
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+## Further Information ##
+
+Further information on the plugin can be found at:
+
+* http://red-folder.blogspot.co.uk/2012/09/phonegap-android-background-service.html
+* http://red-folder.blogspot.com/2012/09/phonegap-android-background-service_11.html
+
+The below is a tutorial to create your own Twitter service:
+
+* http://red-folder.blogspot.com/2012/09/phonegap-service-tutorial-part-1.html
+* http://red-folder.blogspot.com/2012/09/phonegap-service-tutorial-part-2.html
+* http://red-folder.blogspot.com/2012/09/phonegap-service-tutorial-part-3.html
+
+Please let me know your thoughts and comments.
+
+## Licence ##
+
+The MIT License
+
+Copyright (c) 2012 Red Folder Consultancy Ltd
+
+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.
+
+
diff --git a/Android/BackgroundService/1.8.1/backgroundService.js b/Android/BackgroundService/1.8.1/backgroundService.js
new file mode 100644
index 0000000..e6be715
--- /dev/null
+++ b/Android/BackgroundService/1.8.1/backgroundService.js
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2012 Red Folder Consultancy Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Constructor
+ */
+function BackgroundService(serviceName) {
+ var ServiceName = serviceName;
+
+ this.getServiceName = function() {
+ return ServiceName;
+ };
+};
+
+/**
+ * All methods attempt to return the following data in both the success and failure callbacks
+ * Front end development should take into account any all or all of these values may be null
+ *
+ * Following returned in the JSONObject:
+ * Boolean Success - was the call a success
+ * int ErrorCode - Error code if an error occurred, else will be zero
+ * String ErrorMessage - Text representation of the error code
+ * Boolean ServiceRunning - True if the Service is running
+ * Boolean TimerEnabled - True if the Timer is enabled
+ * Boolean RegisteredForBootStart - True if the Service is registered for boot start
+ * JSONObject Configuration - A JSONObject of the configuration of the service (contents dependant on the service)
+ * JSONObject LastestResult - A JSONObject of the last result of the service (contents dependant on the service)
+ */
+
+/**
+ * Starts the Service
+ *
+ * @param successCallback The callback which will be called if the method is successful
+ * @param failureCallback The callback which will be called if the method encounters an error
+ */
+BackgroundService.prototype.startService = function(successCallback, failureCallback) {
+ return cordova.exec( successCallback,
+ failureCallback,
+ 'BackgroundServicePlugin',
+ 'startService',
+ [this.getServiceName()]);
+};
+
+/**
+ * Stops the Service
+ *
+ * @param successCallback The callback which will be called if the method is successful
+ * @param failureCallback The callback which will be called if the method encounters an error
+ */
+BackgroundService.prototype.stopService = function(successCallback, failureCallback) {
+ return cordova.exec( successCallback,
+ failureCallback,
+ 'BackgroundServicePlugin',
+ 'stopService',
+ [this.getServiceName()]);
+};
+
+/**
+ * Enables the Service Timer
+ *
+ * @param milliseconds The milliseconds used for the timer
+ * @param successCallback The callback which will be called if the method is successful
+ * @param failureCallback The callback which will be called if the method encounters an error
+ */
+BackgroundService.prototype.enableTimer = function(milliseconds, successCallback, failureCallback) {
+ return cordova.exec( successCallback,
+ failureCallback,
+ 'BackgroundServicePlugin',
+ 'enableTimer',
+ [this.getServiceName(), milliseconds]);
+};
+
+/**
+ * Disabled the Service Timer
+ *
+ * @param successCallback The callback which will be called if the method is successful
+ * @param failureCallback The callback which will be called if the method encounters an error
+ */
+BackgroundService.prototype.disableTimer = function(successCallback, failureCallback) {
+ return cordova.exec( successCallback,
+ failureCallback,
+ 'BackgroundServicePlugin',
+ 'disableTimer',
+ [this.getServiceName()]);
+};
+
+/**
+ * Sets the configuration for the service
+ *
+ * @oaran configuration JSONObject to be sent to the service
+ * @param successCallback The callback which will be called if the method is successful
+ * @param failureCallback The callback which will be called if the method encounters an error
+ */
+BackgroundService.prototype.setConfiguration = function(configuration, successCallback, failureCallback) {
+ return cordova.exec( successCallback,
+ failureCallback,
+ 'BackgroundServicePlugin',
+ 'setConfiguration',
+ [this.getServiceName(), configuration]);
+};
+
+/**
+ * Registers the service for Boot Start
+ *
+ * @param successCallback The callback which will be called if the method is successful
+ * @param failureCallback The callback which will be called if the method encounters an error
+ */
+BackgroundService.prototype.registerForBootStart = function(successCallback, failureCallback) {
+ return cordova.exec( successCallback,
+ failureCallback,
+ 'BackgroundServicePlugin',
+ 'registerForBootStart',
+ [this.getServiceName()]);
+};
+
+/**
+ * Deregisters the service for Boot Start
+ *
+ * @param successCallback The callback which will be called if the method is successful
+ * @param failureCallback The callback which will be called if the method encounters an error
+ */
+BackgroundService.prototype.deregisterForBootStart = function(successCallback, failureCallback) {
+ return cordova.exec( successCallback,
+ failureCallback,
+ 'BackgroundServicePlugin',
+ 'deregisterForBootStart',
+ [this.getServiceName()]);
+};
+
+/**
+ * Get the current status of the service.
+ *
+ * @param successCallback The callback which will be called if the method is successful
+ * @param failureCallback The callback which will be called if the method encounters an error
+ */
+BackgroundService.prototype.isRegisteredForBootStart = function(successCallback, failureCallback) {
+ return cordova.exec( successCallback,
+ failureCallback,
+ 'BackgroundServicePlugin',
+ 'isRegisteredForBootStart',
+ [this.getServiceName()]);
+};
+
+
+/**
+ * Returns the status of the service
+ *
+ * @param successCallback The callback which will be called if the method is successful
+ * @param failureCallback The callback which will be called if the method encounters an error
+ */
+BackgroundService.prototype.getStatus = function(successCallback, failureCallback) {
+ return cordova.exec( successCallback,
+ failureCallback,
+ 'BackgroundServicePlugin',
+ 'getStatus',
+ [this.getServiceName()]);
+};
+
diff --git a/Android/BackgroundService/1.8.1/backgroundserviceplugin.jar b/Android/BackgroundService/1.8.1/backgroundserviceplugin.jar
new file mode 100644
index 0000000..f61c89d
Binary files /dev/null and b/Android/BackgroundService/1.8.1/backgroundserviceplugin.jar differ
diff --git a/Android/BackgroundService/1.8.1/index.html b/Android/BackgroundService/1.8.1/index.html
new file mode 100644
index 0000000..d50a914
--- /dev/null
+++ b/Android/BackgroundService/1.8.1/index.html
@@ -0,0 +1,219 @@
+
+
+
+
+ MyService
+
+
+
+
+
+
+
+
+
+
+
MyService
+
+
+
+
Service
+
+
+
+
+
Timer
+
+
+
+
+
Boot
+
+
+
+
+
+
Configuration
+
+
+
Hello To
+
+
+
+
+
+
+
+
Latest Result
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/BackgroundService/1.8.1/myService.js b/Android/BackgroundService/1.8.1/myService.js
new file mode 100644
index 0000000..3849f36
--- /dev/null
+++ b/Android/BackgroundService/1.8.1/myService.js
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2012 Red Folder Consultancy Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// ///////////////////
+(function(){
+// ///////////////////
+
+// get local ref to global PhoneGap/Cordova/cordova object for exec function
+var cordovaRef = window.PhoneGap || window.Cordova || window.cordova; // old to new fallbacks
+
+
+cordovaRef.addConstructor(function() {
+ cordovaRef.addPlugin("myService", new BackgroundService('com.yournamespace.yourappname.MyService'));
+});
+
+// ///////////////////
+})();
+// ///////////////////
diff --git a/Android/BackgroundService/2.0.0/MyService.java b/Android/BackgroundService/2.0.0/MyService.java
new file mode 100644
index 0000000..cb39447
--- /dev/null
+++ b/Android/BackgroundService/2.0.0/MyService.java
@@ -0,0 +1,78 @@
+package com.yournamespace.yourappname;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.util.Log;
+
+import com.red_folder.phonegap.plugin.backgroundservice.BackgroundService;
+
+public class MyService extends BackgroundService {
+
+ private final static String TAG = MyService.class.getSimpleName();
+
+ private String mHelloTo = "World";
+
+ @Override
+ protected JSONObject doWork() {
+ JSONObject result = new JSONObject();
+
+ try {
+ SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
+ String now = df.format(new Date(System.currentTimeMillis()));
+
+ String msg = "Hello " + this.mHelloTo + " - its currently " + now;
+ result.put("Message", msg);
+
+ Log.d(TAG, msg);
+ } catch (JSONException e) {
+ }
+
+ return result;
+ }
+
+ @Override
+ protected JSONObject getConfig() {
+ JSONObject result = new JSONObject();
+
+ try {
+ result.put("HelloTo", this.mHelloTo);
+ } catch (JSONException e) {
+ }
+
+ return result;
+ }
+
+ @Override
+ protected void setConfig(JSONObject config) {
+ try {
+ if (config.has("HelloTo"))
+ this.mHelloTo = config.getString("HelloTo");
+ } catch (JSONException e) {
+ }
+
+ }
+
+ @Override
+ protected JSONObject initialiseLatestResult() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected void onTimerEnabled() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ protected void onTimerDisabled() {
+ // TODO Auto-generated method stub
+
+ }
+
+
+}
diff --git a/Android/BackgroundService/2.0.0/README.md b/Android/BackgroundService/2.0.0/README.md
new file mode 100644
index 0000000..a415bcd
--- /dev/null
+++ b/Android/BackgroundService/2.0.0/README.md
@@ -0,0 +1,69 @@
+# Background Service Plugin for Phonegap #
+
+A plugin (and framework code) that allows the development and operation of an Android Background Service.
+
+The example MyService Background Service will write a Hello message to the LogCat every minute. The MyService is designed as sample code.
+
+## Adding the plugin to your project ##
+
+Copy the files to the following locations:
+
+* libs\backgroundserviceplugin-2.0.0.jar
+* src\com\yournamespace\yourappname\MyService.java
+* assets\www\backgroundService-2.0.0.js
+* assets\www\myService-2.0.0.js
+* assets\www\index-2.0.0.html
+
+Add the following to res\xml\config.xml
+
+```
+
+```
+
+Add the following to AndroidManifest.xml
+
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+## Further Information ##
+
+Further information on the plugin can be found at:
+
+* http://red-folder.blogspot.co.uk/2012/09/phonegap-android-background-service.html
+* http://red-folder.blogspot.com/2012/09/phonegap-android-background-service_11.html
+
+The below is a tutorial to create your own Twitter service:
+
+* http://red-folder.blogspot.com/2012/09/phonegap-service-tutorial-part-1.html
+* http://red-folder.blogspot.com/2012/09/phonegap-service-tutorial-part-2.html
+* http://red-folder.blogspot.com/2012/09/phonegap-service-tutorial-part-3.html
+
+Please let me know your thoughts and comments.
+
+## Licence ##
+
+The MIT License
+
+Copyright (c) 2012 Red Folder Consultancy Ltd
+
+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.
+
+
diff --git a/Android/BackgroundService/2.0.0/backgroundService-2.0.0.js b/Android/BackgroundService/2.0.0/backgroundService-2.0.0.js
new file mode 100644
index 0000000..ae70e2c
--- /dev/null
+++ b/Android/BackgroundService/2.0.0/backgroundService-2.0.0.js
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2012 Red Folder Consultancy Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Constructor
+ */
+function CreateBackgroundService(serviceName, require, exports, module) {
+ var exec = require("cordova/exec");
+
+ var BackgroundService = function (serviceName) {
+ var ServiceName = serviceName
+ this.getServiceName = function() {
+ return ServiceName;
+ };
+ };
+
+ var BackgroundServiceError = function (code, message) {
+ this.code = code || null;
+ this.message = message || null;
+ };
+
+ /**
+ * All methods attempt to return the following data in both the success and failure callbacks
+ * Front end development should take into account any all or all of these values may be null
+ *
+ * Following returned in the JSONObject:
+ * Boolean Success - was the call a success
+ * int ErrorCode - Error code if an error occurred, else will be zero
+ * String ErrorMessage - Text representation of the error code
+ * Boolean ServiceRunning - True if the Service is running
+ * Boolean TimerEnabled - True if the Timer is enabled
+ * Boolean RegisteredForBootStart - True if the Service is registered for boot start
+ * JSONObject Configuration - A JSONObject of the configuration of the service (contents dependant on the service)
+ * JSONObject LastestResult - A JSONObject of the last result of the service (contents dependant on the service)
+ */
+
+ /**
+ * Starts the Service
+ *
+ * @param successCallback The callback which will be called if the method is successful
+ * @param failureCallback The callback which will be called if the method encounters an error
+ */
+ BackgroundService.prototype.startService = function(successCallback, failureCallback) {
+ return exec( successCallback,
+ failureCallback,
+ 'BackgroundServicePlugin',
+ 'startService',
+ [this.getServiceName()]);
+ };
+
+ /**
+ * Stops the Service
+ *
+ * @param successCallback The callback which will be called if the method is successful
+ * @param failureCallback The callback which will be called if the method encounters an error
+ */
+ BackgroundService.prototype.stopService = function(successCallback, failureCallback) {
+ return exec( successCallback,
+ failureCallback,
+ 'BackgroundServicePlugin',
+ 'stopService',
+ [this.getServiceName()]);
+ };
+
+ /**
+ * Enables the Service Timer
+ *
+ * @param milliseconds The milliseconds used for the timer
+ * @param successCallback The callback which will be called if the method is successful
+ * @param failureCallback The callback which will be called if the method encounters an error
+ */
+ BackgroundService.prototype.enableTimer = function(milliseconds, successCallback, failureCallback) {
+ return exec( successCallback,
+ failureCallback,
+ 'BackgroundServicePlugin',
+ 'enableTimer',
+ [this.getServiceName(), milliseconds]);
+ };
+
+ /**
+ * Disabled the Service Timer
+ *
+ * @param successCallback The callback which will be called if the method is successful
+ * @param failureCallback The callback which will be called if the method encounters an error
+ */
+ BackgroundService.prototype.disableTimer = function(successCallback, failureCallback) {
+ return exec( successCallback,
+ failureCallback,
+ 'BackgroundServicePlugin',
+ 'disableTimer',
+ [this.getServiceName()]);
+ };
+
+ /**
+ * Sets the configuration for the service
+ *
+ * @oaran configuration JSONObject to be sent to the service
+ * @param successCallback The callback which will be called if the method is successful
+ * @param failureCallback The callback which will be called if the method encounters an error
+ */
+ BackgroundService.prototype.setConfiguration = function(configuration, successCallback, failureCallback) {
+ return exec( successCallback,
+ failureCallback,
+ 'BackgroundServicePlugin',
+ 'setConfiguration',
+ [this.getServiceName(), configuration]);
+ };
+
+ /**
+ * Registers the service for Boot Start
+ *
+ * @param successCallback The callback which will be called if the method is successful
+ * @param failureCallback The callback which will be called if the method encounters an error
+ */
+ BackgroundService.prototype.registerForBootStart = function(successCallback, failureCallback) {
+ return exec( successCallback,
+ failureCallback,
+ 'BackgroundServicePlugin',
+ 'registerForBootStart',
+ [this.getServiceName()]);
+ };
+
+ /**
+ * Deregisters the service for Boot Start
+ *
+ * @param successCallback The callback which will be called if the method is successful
+ * @param failureCallback The callback which will be called if the method encounters an error
+ */
+ BackgroundService.prototype.deregisterForBootStart = function(successCallback, failureCallback) {
+ return exec( successCallback,
+ failureCallback,
+ 'BackgroundServicePlugin',
+ 'deregisterForBootStart',
+ [this.getServiceName()]);
+ };
+
+ /**
+ * Get the current status of the service.
+ *
+ * @param successCallback The callback which will be called if the method is successful
+ * @param failureCallback The callback which will be called if the method encounters an error
+ */
+ BackgroundService.prototype.isRegisteredForBootStart = function(successCallback, failureCallback) {
+ return exec( successCallback,
+ failureCallback,
+ 'BackgroundServicePlugin',
+ 'isRegisteredForBootStart',
+ [this.getServiceName()]);
+ };
+
+
+ /**
+ * Returns the status of the service
+ *
+ * @param successCallback The callback which will be called if the method is successful
+ * @param failureCallback The callback which will be called if the method encounters an error
+ */
+ BackgroundService.prototype.getStatus = function(successCallback, failureCallback) {
+ return exec( successCallback,
+ failureCallback,
+ 'BackgroundServicePlugin',
+ 'getStatus',
+ [this.getServiceName()]);
+ };
+
+ var backgroundService = new BackgroundService(serviceName);
+ module.exports = backgroundService;
+};
diff --git a/Android/BackgroundService/2.0.0/backgroundserviceplugin-2.0.0.jar b/Android/BackgroundService/2.0.0/backgroundserviceplugin-2.0.0.jar
new file mode 100644
index 0000000..0bfceb2
Binary files /dev/null and b/Android/BackgroundService/2.0.0/backgroundserviceplugin-2.0.0.jar differ
diff --git a/Android/BackgroundService/2.0.0/index-2.0.0.html b/Android/BackgroundService/2.0.0/index-2.0.0.html
new file mode 100644
index 0000000..e49998d
--- /dev/null
+++ b/Android/BackgroundService/2.0.0/index-2.0.0.html
@@ -0,0 +1,215 @@
+
+
+
+
+ MyService
+
+
+
+
+
+
+
+
+
+
+
+ IMEI:
+
+
diff --git a/Android/LowLatencyAudio/src/PGLowLatencyAudio.js b/Android/LowLatencyAudio/src/PGLowLatencyAudio.js
index e93513d..5ed4ac7 100644
--- a/Android/LowLatencyAudio/src/PGLowLatencyAudio.js
+++ b/Android/LowLatencyAudio/src/PGLowLatencyAudio.js
@@ -1,27 +1,27 @@
var PGLowLatencyAudio = {
preloadFX: function ( id, assetPath, success, fail) {
- return PhoneGap.exec(success, fail, "PGLowLatencyAudio", "preloadFX", [id, assetPath]);
+ return cordova.exec(success, fail, "PGLowLatencyAudio", "preloadFX", [id, assetPath]);
},
preloadAudio: function ( id, assetPath, voices, success, fail) {
- return PhoneGap.exec(success, fail, "PGLowLatencyAudio", "preloadAudio", [id, assetPath, voices]);
+ return cordova.exec(success, fail, "PGLowLatencyAudio", "preloadAudio", [id, assetPath, voices]);
},
play: function (id, success, fail) {
- return PhoneGap.exec(success, fail, "PGLowLatencyAudio", "play", [id]);
+ return cordova.exec(success, fail, "PGLowLatencyAudio", "play", [id]);
},
stop: function (id, success, fail) {
- return PhoneGap.exec(success, fail, "PGLowLatencyAudio", "stop", [id]);
+ return cordova.exec(success, fail, "PGLowLatencyAudio", "stop", [id]);
},
loop: function (id, success, fail) {
- return PhoneGap.exec(success, fail, "PGLowLatencyAudio", "loop", [id]);
+ return cordova.exec(success, fail, "PGLowLatencyAudio", "loop", [id]);
},
unload: function (id, success, fail) {
- return PhoneGap.exec(success, fail, "PGLowLatencyAudio", "unload", [id]);
+ return cordova.exec(success, fail, "PGLowLatencyAudio", "unload", [id]);
}
diff --git a/Android/LowLatencyAudio/src/com/phonegap/PGLowLatencyAudio.java b/Android/LowLatencyAudio/src/com/phonegap/PGLowLatencyAudio.java
index 8afec40..492dfc3 100644
--- a/Android/LowLatencyAudio/src/com/phonegap/PGLowLatencyAudio.java
+++ b/Android/LowLatencyAudio/src/com/phonegap/PGLowLatencyAudio.java
@@ -22,9 +22,9 @@ import android.media.AudioManager;
import android.media.SoundPool;
import android.util.Log;
-import com.phonegap.api.Plugin;
-import com.phonegap.api.PluginResult;
-import com.phonegap.api.PluginResult.Status;
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
+import org.apache.cordova.api.PluginResult.Status;
/**
* @author triceam
@@ -58,10 +58,12 @@ public class PGLowLatencyAudio extends Plugin {
{
PluginResult result = null;
initSoundPool();
+
try
{
String audioID = data.getString(0);
+ Log.d(audioID, action);
if ( PRELOAD_FX.equals( action ) )
{
@@ -70,7 +72,7 @@ public class PGLowLatencyAudio extends Plugin {
String assetPath =data.getString(1);
String fullPath = "www/".concat( assetPath );
- AssetManager am = ctx.getResources().getAssets();
+ AssetManager am = ctx.getResources().getAssets();
AssetFileDescriptor afd = am.openFd(fullPath);
int assetIntID = soundPool.load(afd, 1);
soundMap.put( audioID , assetIntID );
diff --git a/Android/OcrApiService/com/ocrapiservice/OcrApiServicePlugin.java b/Android/OcrApiService/com/ocrapiservice/OcrApiServicePlugin.java
new file mode 100755
index 0000000..2577ee9
--- /dev/null
+++ b/Android/OcrApiService/com/ocrapiservice/OcrApiServicePlugin.java
@@ -0,0 +1,81 @@
+package com.ocrapiservice;
+
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
+import org.json.JSONArray;
+
+
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.util.Log;
+
+public class OcrApiServicePlugin extends Plugin {
+
+ public static final String NATIVE_ACTION_STRING="convert";
+
+ private String imageUri = "";
+ private String language = "";
+ private String apikey = "";
+ private OcrService apiClient;
+
+ public PluginResult execute(String action, JSONArray data, String callbackId) {
+ if (NATIVE_ACTION_STRING.equals(action)) {
+ try {
+ // Retrieve parameters
+ imageUri = data.getString(0);
+ language = data.getString(1);
+ apikey = data.getString(2);
+
+ // Load the http client and perform the conversion
+ apiClient = new OcrService(apikey);
+
+ String filePath = getFilePathFromResourcePath(imageUri);
+ Log.d("filePath : " , filePath);
+ apiClient.convertToText(language, filePath);
+ if (!checkRequestSucceed())
+ throw new Exception(apiClient.getResponseText());
+
+ // return the text as success
+ String recognizedText = apiClient.getResponseText();
+ return new PluginResult(PluginResult.Status.OK, recognizedText);
+
+ }
+ catch (Exception ex) {
+ return new PluginResult(PluginResult.Status.ERROR, ex.getMessage());
+ }
+
+
+ }
+
+ return null;
+ }
+
+ // check if the request has succeeded
+ private boolean checkRequestSucceed(){
+ if (apiClient.getResponseCode() == 200)
+ return true;
+ else
+ return false;
+ }
+
+ // Convert a resource path to a file path
+ private String getFilePathFromResourcePath(String resourcePath){
+ String filePath = "";
+ // Discussion about the warning
+ // http://simonmacdonald.blogspot.fr/2012/07/phonegap-android-plugins-sometimes-we.html
+ ContentResolver cr = this.cordova.getContext().getContentResolver();
+ String [] projection = {MediaStore.Images.Media.DATA};
+ Cursor cur = cr.query(Uri.parse(resourcePath), projection, null, null, null);
+ if(cur != null)
+ {
+ cur.moveToFirst();
+ filePath = cur.getString(0);
+ }
+
+ return filePath;
+ }
+
+
+}
diff --git a/Android/OcrApiService/com/ocrapiservice/OcrService.java b/Android/OcrApiService/com/ocrapiservice/OcrService.java
new file mode 100755
index 0000000..cf812d7
--- /dev/null
+++ b/Android/OcrApiService/com/ocrapiservice/OcrService.java
@@ -0,0 +1,118 @@
+package com.ocrapiservice;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.mime.MultipartEntity;
+import org.apache.http.entity.mime.content.FileBody;
+import org.apache.http.entity.mime.content.StringBody;
+import org.apache.http.impl.client.DefaultHttpClient;
+
+public class OcrService {
+ public final String SERVICE_URL = "http://api.ocrapiservice.com/1.0/rest/ocr";
+
+ private final String PARAM_IMAGE = "image";
+ private final String PARAM_LANGUAGE = "language";
+ private final String PARAM_APIKEY = "apikey";
+
+ private String apiKey;
+
+ private int responseCode;
+ private String responseText;
+
+ public OcrService(final String apiKey) {
+ this.apiKey = apiKey;
+ }
+
+ /*
+ * Convert image to text.
+ *
+ * @param language The image text language.
+ * @param filePath The image absolute file path.
+ *
+ * @return true if everything went okay and false if there is an error with sending and receiving data.
+ */
+ public boolean convertToText(final String language, final String filePath) {
+ try {
+ sendImage(language, filePath);
+
+ return true;
+ } catch (ClientProtocolException e) {
+ e.printStackTrace();
+
+ return false;
+ } catch (IOException e) {
+ e.printStackTrace();
+
+ return false;
+ }
+ }
+
+ /*
+ * Send image to OCR service and read response.
+ *
+ * @param language The image text language.
+ * @param filePath The image absolute file path.
+ *
+ */
+ private void sendImage(final String language, final String filePath) throws ClientProtocolException, IOException {
+ final HttpClient httpclient = new DefaultHttpClient();
+ final HttpPost httppost = new HttpPost(SERVICE_URL);
+
+ final FileBody image = new FileBody(new File(filePath));
+
+ final MultipartEntity reqEntity = new MultipartEntity();
+ reqEntity.addPart(PARAM_IMAGE, image);
+ reqEntity.addPart(PARAM_LANGUAGE, new StringBody(language));
+ reqEntity.addPart(PARAM_APIKEY, new StringBody(getApiKey()));
+ httppost.setEntity(reqEntity);
+
+ final HttpResponse response = httpclient.execute(httppost);
+ final HttpEntity resEntity = response.getEntity();
+ final StringBuilder sb = new StringBuilder();
+ if (resEntity != null) {
+ final InputStream stream = resEntity.getContent();
+ byte bytes[] = new byte[4096];
+ int numBytes;
+ while ((numBytes=stream.read(bytes))!=-1) {
+ if (numBytes!=0) {
+ sb.append(new String(bytes, 0, numBytes));
+ }
+ }
+ }
+
+ setResponseCode(response.getStatusLine().getStatusCode());
+
+ setResponseText(sb.toString());
+ }
+
+ public int getResponseCode() {
+ return responseCode;
+ }
+
+ public void setResponseCode(int responseCode) {
+ this.responseCode = responseCode;
+ }
+
+ public String getResponseText() {
+ return responseText;
+ }
+
+ public void setResponseText(String responseText) {
+ this.responseText = responseText;
+ }
+
+ public String getApiKey() {
+ return apiKey;
+ }
+
+ public void setApiKey(String apiKey) {
+ this.apiKey = apiKey;
+ }
+}
diff --git a/Android/OcrApiService/ocrapiservice.js b/Android/OcrApiService/ocrapiservice.js
new file mode 100755
index 0000000..ecfb894
--- /dev/null
+++ b/Android/OcrApiService/ocrapiservice.js
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2012 by Guillaume Charhon
+ */
+var ocrapiservice = {
+
+ // parameters
+ // - imageURI : URI of the image to convert
+ // - language : 2 letter language code (ex : "en" for english)
+ // - apikey : Your api key displayed in your http://ocrapiservice.com dashboard
+
+ convert: function (success, fail, imageURI,language, apikey ) {
+ return cordova.exec( success, fail,
+ "OcrApiServicePlugin",
+ "convert", [imageURI, language, apikey]);
+ }
+};
\ No newline at end of file
diff --git a/Android/OcrApiService/org/apache/http/entity/mime/FormBodyPart.java b/Android/OcrApiService/org/apache/http/entity/mime/FormBodyPart.java
new file mode 100755
index 0000000..dc67c05
--- /dev/null
+++ b/Android/OcrApiService/org/apache/http/entity/mime/FormBodyPart.java
@@ -0,0 +1,109 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+
+package org.apache.http.entity.mime;
+
+import org.apache.http.entity.mime.content.ContentBody;
+
+/**
+ * FormBodyPart class represents a content body that can be used as a part of multipart encoded
+ * entities. This class automatically populates the header with standard fields based on
+ * the content description of the enclosed body.
+ *
+ * @since 4.0
+ */
+public class FormBodyPart {
+
+ private final String name;
+ private final Header header;
+
+ private final ContentBody body;
+
+ public FormBodyPart(final String name, final ContentBody body) {
+ super();
+ if (name == null) {
+ throw new IllegalArgumentException("Name may not be null");
+ }
+ if (body == null) {
+ throw new IllegalArgumentException("Body may not be null");
+ }
+ this.name = name;
+ this.body = body;
+ this.header = new Header();
+
+ generateContentDisp(body);
+ generateContentType(body);
+ generateTransferEncoding(body);
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public ContentBody getBody() {
+ return this.body;
+ }
+
+ public Header getHeader() {
+ return this.header;
+ }
+
+ public void addField(final String name, final String value) {
+ if (name == null) {
+ throw new IllegalArgumentException("Field name may not be null");
+ }
+ this.header.addField(new MinimalField(name, value));
+ }
+
+ protected void generateContentDisp(final ContentBody body) {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("form-data; name=\"");
+ buffer.append(getName());
+ buffer.append("\"");
+ if (body.getFilename() != null) {
+ buffer.append("; filename=\"");
+ buffer.append(body.getFilename());
+ buffer.append("\"");
+ }
+ addField(MIME.CONTENT_DISPOSITION, buffer.toString());
+ }
+
+ protected void generateContentType(final ContentBody body) {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append(body.getMimeType()); // MimeType cannot be null
+ if (body.getCharset() != null) { // charset may legitimately be null
+ buffer.append("; charset=");
+ buffer.append(body.getCharset());
+ }
+ addField(MIME.CONTENT_TYPE, buffer.toString());
+ }
+
+ protected void generateTransferEncoding(final ContentBody body) {
+ addField(MIME.CONTENT_TRANSFER_ENC, body.getTransferEncoding()); // TE cannot be null
+ }
+
+}
diff --git a/Android/OcrApiService/org/apache/http/entity/mime/Header.java b/Android/OcrApiService/org/apache/http/entity/mime/Header.java
new file mode 100755
index 0000000..2c4c6dd
--- /dev/null
+++ b/Android/OcrApiService/org/apache/http/entity/mime/Header.java
@@ -0,0 +1,145 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+
+package org.apache.http.entity.mime;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * The header of an entity (see RFC 2045).
+ */
+public class Header implements Iterable {
+
+ private final List fields;
+ private final Map> fieldMap;
+
+ public Header() {
+ super();
+ this.fields = new LinkedList();
+ this.fieldMap = new HashMap>();
+ }
+
+ public void addField(final MinimalField field) {
+ if (field == null) {
+ return;
+ }
+ String key = field.getName().toLowerCase(Locale.US);
+ List values = this.fieldMap.get(key);
+ if (values == null) {
+ values = new LinkedList();
+ this.fieldMap.put(key, values);
+ }
+ values.add(field);
+ this.fields.add(field);
+ }
+
+ public List getFields() {
+ return new ArrayList(this.fields);
+ }
+
+ public MinimalField getField(final String name) {
+ if (name == null) {
+ return null;
+ }
+ String key = name.toLowerCase(Locale.US);
+ List list = this.fieldMap.get(key);
+ if (list != null && !list.isEmpty()) {
+ return list.get(0);
+ }
+ return null;
+ }
+
+ public List getFields(final String name) {
+ if (name == null) {
+ return null;
+ }
+ String key = name.toLowerCase(Locale.US);
+ List list = this.fieldMap.get(key);
+ if (list == null || list.isEmpty()) {
+ return Collections.emptyList();
+ } else {
+ return new ArrayList(list);
+ }
+ }
+
+ public int removeFields(final String name) {
+ if (name == null) {
+ return 0;
+ }
+ String key = name.toLowerCase(Locale.US);
+ List removed = fieldMap.remove(key);
+ if (removed == null || removed.isEmpty()) {
+ return 0;
+ }
+ this.fields.removeAll(removed);
+ return removed.size();
+ }
+
+ public void setField(final MinimalField field) {
+ if (field == null) {
+ return;
+ }
+ String key = field.getName().toLowerCase(Locale.US);
+ List list = fieldMap.get(key);
+ if (list == null || list.isEmpty()) {
+ addField(field);
+ return;
+ }
+ list.clear();
+ list.add(field);
+ int firstOccurrence = -1;
+ int index = 0;
+ for (Iterator it = this.fields.iterator(); it.hasNext(); index++) {
+ MinimalField f = it.next();
+ if (f.getName().equalsIgnoreCase(field.getName())) {
+ it.remove();
+ if (firstOccurrence == -1) {
+ firstOccurrence = index;
+ }
+ }
+ }
+ this.fields.add(firstOccurrence, field);
+ }
+
+ public Iterator iterator() {
+ return Collections.unmodifiableList(fields).iterator();
+ }
+
+ @Override
+ public String toString() {
+ return this.fields.toString();
+ }
+
+}
+
diff --git a/Android/OcrApiService/org/apache/http/entity/mime/HttpMultipart.java b/Android/OcrApiService/org/apache/http/entity/mime/HttpMultipart.java
new file mode 100755
index 0000000..ce7f2d8
--- /dev/null
+++ b/Android/OcrApiService/org/apache/http/entity/mime/HttpMultipart.java
@@ -0,0 +1,262 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+
+package org.apache.http.entity.mime;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.http.entity.mime.content.ContentBody;
+import org.apache.http.util.ByteArrayBuffer;
+
+/**
+ * HttpMultipart represents a collection of MIME multipart encoded content bodies. This class is
+ * capable of operating either in the strict (RFC 822, RFC 2045, RFC 2046 compliant) or
+ * the browser compatible modes.
+ *
+ * @since 4.0
+ */
+public class HttpMultipart {
+
+ private static ByteArrayBuffer encode(
+ final Charset charset, final String string) {
+ ByteBuffer encoded = charset.encode(CharBuffer.wrap(string));
+ ByteArrayBuffer bab = new ByteArrayBuffer(encoded.remaining());
+ bab.append(encoded.array(), encoded.position(), encoded.remaining());
+ return bab;
+ }
+
+ private static void writeBytes(
+ final ByteArrayBuffer b, final OutputStream out) throws IOException {
+ out.write(b.buffer(), 0, b.length());
+ }
+
+ private static void writeBytes(
+ final String s, final Charset charset, final OutputStream out) throws IOException {
+ ByteArrayBuffer b = encode(charset, s);
+ writeBytes(b, out);
+ }
+
+ private static void writeBytes(
+ final String s, final OutputStream out) throws IOException {
+ ByteArrayBuffer b = encode(MIME.DEFAULT_CHARSET, s);
+ writeBytes(b, out);
+ }
+
+ private static void writeField(
+ final MinimalField field, final OutputStream out) throws IOException {
+ writeBytes(field.getName(), out);
+ writeBytes(FIELD_SEP, out);
+ writeBytes(field.getBody(), out);
+ writeBytes(CR_LF, out);
+ }
+
+ private static void writeField(
+ final MinimalField field, final Charset charset, final OutputStream out) throws IOException {
+ writeBytes(field.getName(), charset, out);
+ writeBytes(FIELD_SEP, out);
+ writeBytes(field.getBody(), charset, out);
+ writeBytes(CR_LF, out);
+ }
+
+ private static final ByteArrayBuffer FIELD_SEP = encode(MIME.DEFAULT_CHARSET, ": ");
+ private static final ByteArrayBuffer CR_LF = encode(MIME.DEFAULT_CHARSET, "\r\n");
+ private static final ByteArrayBuffer TWO_DASHES = encode(MIME.DEFAULT_CHARSET, "--");
+
+
+ private final String subType;
+ private final Charset charset;
+ private final String boundary;
+ private final List parts;
+
+ private final HttpMultipartMode mode;
+
+ /**
+ * Creates an instance with the specified settings.
+ *
+ * @param subType mime subtype - must not be {@code null}
+ * @param charset the character set to use. May be {@code null}, in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
+ * @param boundary to use - must not be {@code null}
+ * @param mode the mode to use
+ * @throws IllegalArgumentException if charset is null or boundary is null
+ */
+ public HttpMultipart(final String subType, final Charset charset, final String boundary, HttpMultipartMode mode) {
+ super();
+ if (subType == null) {
+ throw new IllegalArgumentException("Multipart subtype may not be null");
+ }
+ if (boundary == null) {
+ throw new IllegalArgumentException("Multipart boundary may not be null");
+ }
+ this.subType = subType;
+ this.charset = charset != null ? charset : MIME.DEFAULT_CHARSET;
+ this.boundary = boundary;
+ this.parts = new ArrayList();
+ this.mode = mode;
+ }
+
+ /**
+ * Creates an instance with the specified settings.
+ * Mode is set to {@link HttpMultipartMode#STRICT}
+ *
+ * @param subType mime subtype - must not be {@code null}
+ * @param charset the character set to use. May be {@code null}, in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
+ * @param boundary to use - must not be {@code null}
+ * @throws IllegalArgumentException if charset is null or boundary is null
+ */
+ public HttpMultipart(final String subType, final Charset charset, final String boundary) {
+ this(subType, charset, boundary, HttpMultipartMode.STRICT);
+ }
+
+ public HttpMultipart(final String subType, final String boundary) {
+ this(subType, null, boundary);
+ }
+
+ public String getSubType() {
+ return this.subType;
+ }
+
+ public Charset getCharset() {
+ return this.charset;
+ }
+
+ public HttpMultipartMode getMode() {
+ return this.mode;
+ }
+
+ public List getBodyParts() {
+ return this.parts;
+ }
+
+ public void addBodyPart(final FormBodyPart part) {
+ if (part == null) {
+ return;
+ }
+ this.parts.add(part);
+ }
+
+ public String getBoundary() {
+ return this.boundary;
+ }
+
+ private void doWriteTo(
+ final HttpMultipartMode mode,
+ final OutputStream out,
+ boolean writeContent) throws IOException {
+
+ ByteArrayBuffer boundary = encode(this.charset, getBoundary());
+ for (FormBodyPart part: this.parts) {
+ writeBytes(TWO_DASHES, out);
+ writeBytes(boundary, out);
+ writeBytes(CR_LF, out);
+
+ Header header = part.getHeader();
+
+ switch (mode) {
+ case STRICT:
+ for (MinimalField field: header) {
+ writeField(field, out);
+ }
+ break;
+ case BROWSER_COMPATIBLE:
+ // Only write Content-Disposition
+ // Use content charset
+ MinimalField cd = part.getHeader().getField(MIME.CONTENT_DISPOSITION);
+ writeField(cd, this.charset, out);
+ String filename = part.getBody().getFilename();
+ if (filename != null) {
+ MinimalField ct = part.getHeader().getField(MIME.CONTENT_TYPE);
+ writeField(ct, this.charset, out);
+ }
+ break;
+ }
+ writeBytes(CR_LF, out);
+
+ if (writeContent) {
+ part.getBody().writeTo(out);
+ }
+ writeBytes(CR_LF, out);
+ }
+ writeBytes(TWO_DASHES, out);
+ writeBytes(boundary, out);
+ writeBytes(TWO_DASHES, out);
+ writeBytes(CR_LF, out);
+ }
+
+ /**
+ * Writes out the content in the multipart/form encoding. This method
+ * produces slightly different formatting depending on its compatibility
+ * mode.
+ *
+ * @see #getMode()
+ */
+ public void writeTo(final OutputStream out) throws IOException {
+ doWriteTo(this.mode, out, true);
+ }
+
+ /**
+ * Determines the total length of the multipart content (content length of
+ * individual parts plus that of extra elements required to delimit the parts
+ * from one another). If any of the @{link BodyPart}s contained in this object
+ * is of a streaming entity of unknown length the total length is also unknown.
+ *
+ * This method buffers only a small amount of data in order to determine the
+ * total length of the entire entity. The content of individual parts is not
+ * buffered.
+ *
+ * @return total length of the multipart entity if known, -1
+ * otherwise.
+ */
+ public long getTotalLength() {
+ long contentLen = 0;
+ for (FormBodyPart part: this.parts) {
+ ContentBody body = part.getBody();
+ long len = body.getContentLength();
+ if (len >= 0) {
+ contentLen += len;
+ } else {
+ return -1;
+ }
+ }
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ doWriteTo(this.mode, out, false);
+ byte[] extra = out.toByteArray();
+ return contentLen + extra.length;
+ } catch (IOException ex) {
+ // Should never happen
+ return -1;
+ }
+ }
+
+}
diff --git a/Android/OcrApiService/org/apache/http/entity/mime/HttpMultipartMode.java b/Android/OcrApiService/org/apache/http/entity/mime/HttpMultipartMode.java
new file mode 100755
index 0000000..13e15e0
--- /dev/null
+++ b/Android/OcrApiService/org/apache/http/entity/mime/HttpMultipartMode.java
@@ -0,0 +1,41 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+
+package org.apache.http.entity.mime;
+
+/**
+ *
+ * @since 4.0
+ */
+public enum HttpMultipartMode {
+
+ /** RFC 822, RFC 2045, RFC 2046 compliant */
+ STRICT,
+ /** browser-compatible mode, i.e. only write Content-Disposition; use content charset */
+ BROWSER_COMPATIBLE
+
+}
diff --git a/Android/OcrApiService/org/apache/http/entity/mime/MIME.java b/Android/OcrApiService/org/apache/http/entity/mime/MIME.java
new file mode 100755
index 0000000..775e397
--- /dev/null
+++ b/Android/OcrApiService/org/apache/http/entity/mime/MIME.java
@@ -0,0 +1,48 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+
+package org.apache.http.entity.mime;
+
+import java.nio.charset.Charset;
+
+/**
+ *
+ * @since 4.0
+ */
+public final class MIME {
+
+ public static final String CONTENT_TYPE = "Content-Type";
+ public static final String CONTENT_TRANSFER_ENC = "Content-Transfer-Encoding";
+ public static final String CONTENT_DISPOSITION = "Content-Disposition";
+
+ public static final String ENC_8BIT = "8bit";
+ public static final String ENC_BINARY = "binary";
+
+ /** The default character set to be used, i.e. "US-ASCII" */
+ public static final Charset DEFAULT_CHARSET = Charset.forName("US-ASCII");
+
+}
diff --git a/Android/OcrApiService/org/apache/http/entity/mime/MinimalField.java b/Android/OcrApiService/org/apache/http/entity/mime/MinimalField.java
new file mode 100755
index 0000000..8fb160c
--- /dev/null
+++ b/Android/OcrApiService/org/apache/http/entity/mime/MinimalField.java
@@ -0,0 +1,63 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+
+package org.apache.http.entity.mime;
+
+/**
+ * Minimal MIME field.
+ *
+ * @since 4.0
+ */
+public class MinimalField {
+
+ private final String name;
+ private final String value;
+
+ MinimalField(final String name, final String value) {
+ super();
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public String getBody() {
+ return this.value;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append(this.name);
+ buffer.append(": ");
+ buffer.append(this.value);
+ return buffer.toString();
+ }
+
+}
diff --git a/Android/OcrApiService/org/apache/http/entity/mime/MultipartEntity.java b/Android/OcrApiService/org/apache/http/entity/mime/MultipartEntity.java
new file mode 100755
index 0000000..deed644
--- /dev/null
+++ b/Android/OcrApiService/org/apache/http/entity/mime/MultipartEntity.java
@@ -0,0 +1,186 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+
+package org.apache.http.entity.mime;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.util.Random;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.entity.mime.content.ContentBody;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * Multipart/form coded HTTP entity consisting of multiple body parts.
+ *
+ * @since 4.0
+ */
+public class MultipartEntity implements HttpEntity {
+
+ /**
+ * The pool of ASCII chars to be used for generating a multipart boundary.
+ */
+ private final static char[] MULTIPART_CHARS =
+ "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ .toCharArray();
+
+ private final HttpMultipart multipart;
+ private final Header contentType;
+
+ // @GuardedBy("dirty") // we always read dirty before accessing length
+ private long length;
+ private volatile boolean dirty; // used to decide whether to recalculate length
+
+ /**
+ * Creates an instance using the specified parameters
+ * @param mode the mode to use, may be {@code null}, in which case {@link HttpMultipartMode#STRICT} is used
+ * @param boundary the boundary string, may be {@code null}, in which case {@link #generateBoundary()} is invoked to create the string
+ * @param charset the character set to use, may be {@code null}, in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
+ */
+ public MultipartEntity(
+ HttpMultipartMode mode,
+ String boundary,
+ Charset charset) {
+ super();
+ if (boundary == null) {
+ boundary = generateBoundary();
+ }
+ if (mode == null) {
+ mode = HttpMultipartMode.STRICT;
+ }
+ this.multipart = new HttpMultipart("form-data", charset, boundary, mode);
+ this.contentType = new BasicHeader(
+ HTTP.CONTENT_TYPE,
+ generateContentType(boundary, charset));
+ this.dirty = true;
+ }
+
+ /**
+ * Creates an instance using the specified {@link HttpMultipartMode} mode.
+ * Boundary and charset are set to {@code null}.
+ * @param mode the desired mode
+ */
+ public MultipartEntity(final HttpMultipartMode mode) {
+ this(mode, null, null);
+ }
+
+ /**
+ * Creates an instance using mode {@link HttpMultipartMode#STRICT}
+ */
+ public MultipartEntity() {
+ this(HttpMultipartMode.STRICT, null, null);
+ }
+
+ protected String generateContentType(
+ final String boundary,
+ final Charset charset) {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("multipart/form-data; boundary=");
+ buffer.append(boundary);
+ if (charset != null) {
+ buffer.append("; charset=");
+ buffer.append(charset.name());
+ }
+ return buffer.toString();
+ }
+
+ protected String generateBoundary() {
+ StringBuilder buffer = new StringBuilder();
+ Random rand = new Random();
+ int count = rand.nextInt(11) + 30; // a random size from 30 to 40
+ for (int i = 0; i < count; i++) {
+ buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
+ }
+ return buffer.toString();
+ }
+
+ public void addPart(final FormBodyPart bodyPart) {
+ this.multipart.addBodyPart(bodyPart);
+ this.dirty = true;
+ }
+
+ public void addPart(final String name, final ContentBody contentBody) {
+ addPart(new FormBodyPart(name, contentBody));
+ }
+
+ public boolean isRepeatable() {
+ for (FormBodyPart part: this.multipart.getBodyParts()) {
+ ContentBody body = part.getBody();
+ if (body.getContentLength() < 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean isChunked() {
+ return !isRepeatable();
+ }
+
+ public boolean isStreaming() {
+ return !isRepeatable();
+ }
+
+ public long getContentLength() {
+ if (this.dirty) {
+ this.length = this.multipart.getTotalLength();
+ this.dirty = false;
+ }
+ return this.length;
+ }
+
+ public Header getContentType() {
+ return this.contentType;
+ }
+
+ public Header getContentEncoding() {
+ return null;
+ }
+
+ public void consumeContent()
+ throws IOException, UnsupportedOperationException{
+ if (isStreaming()) {
+ throw new UnsupportedOperationException(
+ "Streaming entity does not implement #consumeContent()");
+ }
+ }
+
+ public InputStream getContent() throws IOException, UnsupportedOperationException {
+ throw new UnsupportedOperationException(
+ "Multipart form entity does not implement #getContent()");
+ }
+
+ public void writeTo(final OutputStream outstream) throws IOException {
+ this.multipart.writeTo(outstream);
+ }
+
+}
diff --git a/Android/OcrApiService/org/apache/http/entity/mime/content/AbstractContentBody.java b/Android/OcrApiService/org/apache/http/entity/mime/content/AbstractContentBody.java
new file mode 100755
index 0000000..3b55773
--- /dev/null
+++ b/Android/OcrApiService/org/apache/http/entity/mime/content/AbstractContentBody.java
@@ -0,0 +1,68 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+
+package org.apache.http.entity.mime.content;
+
+/**
+ *
+ * @since 4.0
+ */
+public abstract class AbstractContentBody implements ContentBody {
+
+ private final String mimeType;
+ private final String mediaType;
+ private final String subType;
+
+ public AbstractContentBody(final String mimeType) {
+ super();
+ if (mimeType == null) {
+ throw new IllegalArgumentException("MIME type may not be null");
+ }
+ this.mimeType = mimeType;
+ int i = mimeType.indexOf('/');
+ if (i != -1) {
+ this.mediaType = mimeType.substring(0, i);
+ this.subType = mimeType.substring(i + 1);
+ } else {
+ this.mediaType = mimeType;
+ this.subType = null;
+ }
+ }
+
+ public String getMimeType() {
+ return this.mimeType;
+ }
+
+ public String getMediaType() {
+ return this.mediaType;
+ }
+
+ public String getSubType() {
+ return this.subType;
+ }
+
+}
diff --git a/Android/OcrApiService/org/apache/http/entity/mime/content/ByteArrayBody.java b/Android/OcrApiService/org/apache/http/entity/mime/content/ByteArrayBody.java
new file mode 100755
index 0000000..863c254
--- /dev/null
+++ b/Android/OcrApiService/org/apache/http/entity/mime/content/ByteArrayBody.java
@@ -0,0 +1,98 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+package org.apache.http.entity.mime.content;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.http.entity.mime.MIME;
+import org.apache.http.entity.mime.content.AbstractContentBody;
+
+/**
+ * Body part that is built using a byte array containing a file.
+ *
+ * @since 4.1
+ */
+public class ByteArrayBody extends AbstractContentBody {
+
+ /**
+ * The contents of the file contained in this part.
+ */
+ private final byte[] data;
+
+ /**
+ * The name of the file contained in this part.
+ */
+ private final String filename;
+
+ /**
+ * Creates a new ByteArrayBody.
+ *
+ * @param data The contents of the file contained in this part.
+ * @param mimeType The mime type of the file contained in this part.
+ * @param filename The name of the file contained in this part.
+ */
+ public ByteArrayBody(final byte[] data, final String mimeType, final String filename) {
+ super(mimeType);
+ if (data == null) {
+ throw new IllegalArgumentException("byte[] may not be null");
+ }
+ this.data = data;
+ this.filename = filename;
+ }
+
+ /**
+ * Creates a new ByteArrayBody.
+ *
+ * @param data The contents of the file contained in this part.
+ * @param filename The name of the file contained in this part.
+ */
+ public ByteArrayBody(final byte[] data, final String filename) {
+ this(data, "application/octet-stream", filename);
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public void writeTo(final OutputStream out) throws IOException {
+ out.write(data);
+ }
+
+ public String getCharset() {
+ return null;
+ }
+
+ public String getTransferEncoding() {
+ return MIME.ENC_BINARY;
+ }
+
+ public long getContentLength() {
+ return data.length;
+ }
+
+}
diff --git a/Android/OcrApiService/org/apache/http/entity/mime/content/ContentBody.java b/Android/OcrApiService/org/apache/http/entity/mime/content/ContentBody.java
new file mode 100755
index 0000000..58076f9
--- /dev/null
+++ b/Android/OcrApiService/org/apache/http/entity/mime/content/ContentBody.java
@@ -0,0 +1,43 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+
+package org.apache.http.entity.mime.content;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ *
+ * @since 4.0
+ */
+public interface ContentBody extends ContentDescriptor {
+
+ String getFilename();
+
+ void writeTo(OutputStream out) throws IOException;
+
+}
diff --git a/Android/OcrApiService/org/apache/http/entity/mime/content/ContentDescriptor.java b/Android/OcrApiService/org/apache/http/entity/mime/content/ContentDescriptor.java
new file mode 100755
index 0000000..6320d38
--- /dev/null
+++ b/Android/OcrApiService/org/apache/http/entity/mime/content/ContentDescriptor.java
@@ -0,0 +1,89 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+
+package org.apache.http.entity.mime.content;
+
+/**
+ * Represents common content properties.
+ */
+public interface ContentDescriptor {
+
+ /**
+ * Returns the body descriptors MIME type.
+ * @see #getMediaType()
+ * @see #getSubType()
+ * @return The MIME type, which has been parsed from the
+ * content-type definition. Must not be null, but
+ * "text/plain", if no content-type was specified.
+ */
+ String getMimeType();
+
+ /**
+ * Gets the defaulted MIME media type for this content.
+ * For example TEXT, IMAGE, MULTIPART
+ * @see #getMimeType()
+ * @return the MIME media type when content-type specified,
+ * otherwise the correct default (TEXT)
+ */
+ String getMediaType();
+
+ /**
+ * Gets the defaulted MIME sub type for this content.
+ * @see #getMimeType()
+ * @return the MIME media type when content-type is specified,
+ * otherwise the correct default (PLAIN)
+ */
+ String getSubType();
+
+ /**
+ *
The body descriptors character set, defaulted appropriately for the MIME type.
+ *
+ * For TEXT types, this will be defaulted to us-ascii.
+ * For other types, when the charset parameter is missing this property will be null.
+ *
+ * @return Character set, which has been parsed from the
+ * content-type definition. Not null for TEXT types, when unset will
+ * be set to default us-ascii. For other types, when unset,
+ * null will be returned.
+ */
+ String getCharset();
+
+ /**
+ * Returns the body descriptors transfer encoding.
+ * @return The transfer encoding. Must not be null, but "7bit",
+ * if no transfer-encoding was specified.
+ */
+ String getTransferEncoding();
+
+ /**
+ * Returns the body descriptors content-length.
+ * @return Content length, if known, or -1, to indicate the absence of a
+ * content-length header.
+ */
+ long getContentLength();
+
+}
diff --git a/Android/OcrApiService/org/apache/http/entity/mime/content/FileBody.java b/Android/OcrApiService/org/apache/http/entity/mime/content/FileBody.java
new file mode 100755
index 0000000..206bc78
--- /dev/null
+++ b/Android/OcrApiService/org/apache/http/entity/mime/content/FileBody.java
@@ -0,0 +1,125 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+
+package org.apache.http.entity.mime.content;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.http.entity.mime.MIME;
+
+/**
+ *
+ * @since 4.0
+ */
+public class FileBody extends AbstractContentBody {
+
+ private final File file;
+ private final String filename;
+ private final String charset;
+
+ /**
+ * @since 4.1
+ */
+ public FileBody(final File file,
+ final String filename,
+ final String mimeType,
+ final String charset) {
+ super(mimeType);
+ if (file == null) {
+ throw new IllegalArgumentException("File may not be null");
+ }
+ this.file = file;
+ if (filename != null)
+ this.filename = filename;
+ else
+ this.filename = file.getName();
+ this.charset = charset;
+ }
+
+ /**
+ * @since 4.1
+ */
+ public FileBody(final File file,
+ final String mimeType,
+ final String charset) {
+ this(file, null, mimeType, charset);
+ }
+
+ public FileBody(final File file, final String mimeType) {
+ this(file, mimeType, null);
+ }
+
+ public FileBody(final File file) {
+ this(file, "application/octet-stream");
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return new FileInputStream(this.file);
+ }
+
+ public void writeTo(final OutputStream out) throws IOException {
+ if (out == null) {
+ throw new IllegalArgumentException("Output stream may not be null");
+ }
+ InputStream in = new FileInputStream(this.file);
+ try {
+ byte[] tmp = new byte[4096];
+ int l;
+ while ((l = in.read(tmp)) != -1) {
+ out.write(tmp, 0, l);
+ }
+ out.flush();
+ } finally {
+ in.close();
+ }
+ }
+
+ public String getTransferEncoding() {
+ return MIME.ENC_BINARY;
+ }
+
+ public String getCharset() {
+ return charset;
+ }
+
+ public long getContentLength() {
+ return this.file.length();
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public File getFile() {
+ return this.file;
+ }
+
+}
diff --git a/Android/OcrApiService/org/apache/http/entity/mime/content/InputStreamBody.java b/Android/OcrApiService/org/apache/http/entity/mime/content/InputStreamBody.java
new file mode 100755
index 0000000..db1b4dd
--- /dev/null
+++ b/Android/OcrApiService/org/apache/http/entity/mime/content/InputStreamBody.java
@@ -0,0 +1,94 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+
+package org.apache.http.entity.mime.content;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.http.entity.mime.MIME;
+
+/**
+ *
+ * @since 4.0
+ */
+public class InputStreamBody extends AbstractContentBody {
+
+ private final InputStream in;
+ private final String filename;
+
+ public InputStreamBody(final InputStream in, final String mimeType, final String filename) {
+ super(mimeType);
+ if (in == null) {
+ throw new IllegalArgumentException("Input stream may not be null");
+ }
+ this.in = in;
+ this.filename = filename;
+ }
+
+ public InputStreamBody(final InputStream in, final String filename) {
+ this(in, "application/octet-stream", filename);
+ }
+
+ public InputStream getInputStream() {
+ return this.in;
+ }
+
+ public void writeTo(final OutputStream out) throws IOException {
+ if (out == null) {
+ throw new IllegalArgumentException("Output stream may not be null");
+ }
+ try {
+ byte[] tmp = new byte[4096];
+ int l;
+ while ((l = this.in.read(tmp)) != -1) {
+ out.write(tmp, 0, l);
+ }
+ out.flush();
+ } finally {
+ this.in.close();
+ }
+ }
+
+ public String getTransferEncoding() {
+ return MIME.ENC_BINARY;
+ }
+
+ public String getCharset() {
+ return null;
+ }
+
+ public long getContentLength() {
+ return -1;
+ }
+
+ public String getFilename() {
+ return this.filename;
+ }
+
+}
diff --git a/Android/OcrApiService/org/apache/http/entity/mime/content/StringBody.java b/Android/OcrApiService/org/apache/http/entity/mime/content/StringBody.java
new file mode 100755
index 0000000..8ea90bf
--- /dev/null
+++ b/Android/OcrApiService/org/apache/http/entity/mime/content/StringBody.java
@@ -0,0 +1,164 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ */
+
+package org.apache.http.entity.mime.content;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+
+import org.apache.http.entity.mime.MIME;
+
+/**
+ *
+ * @since 4.0
+ */
+public class StringBody extends AbstractContentBody {
+
+ private final byte[] content;
+ private final Charset charset;
+
+ /**
+ * @since 4.1
+ */
+ public static StringBody create(
+ final String text,
+ final String mimeType,
+ final Charset charset) throws IllegalArgumentException {
+ try {
+ return new StringBody(text, mimeType, charset);
+ } catch (UnsupportedEncodingException ex) {
+ throw new IllegalArgumentException("Charset " + charset + " is not supported", ex);
+ }
+ }
+
+ /**
+ * @since 4.1
+ */
+ public static StringBody create(
+ final String text, final Charset charset) throws IllegalArgumentException {
+ return create(text, null, charset);
+ }
+
+ /**
+ * @since 4.1
+ */
+ public static StringBody create(final String text) throws IllegalArgumentException {
+ return create(text, null, null);
+ }
+
+ /**
+ * Create a StringBody from the specified text, mime type and character set.
+ *
+ * @param text to be used for the body, not {@code null}
+ * @param mimeType the mime type, not {@code null}
+ * @param charset the character set, may be {@code null}, in which case the US-ASCII charset is used
+ * @throws UnsupportedEncodingException
+ * @throws IllegalArgumentException if the {@code text} parameter is null
+ */
+ public StringBody(
+ final String text,
+ final String mimeType,
+ Charset charset) throws UnsupportedEncodingException {
+ super(mimeType);
+ if (text == null) {
+ throw new IllegalArgumentException("Text may not be null");
+ }
+ if (charset == null) {
+ charset = Charset.forName("US-ASCII");
+ }
+ this.content = text.getBytes(charset.name());
+ this.charset = charset;
+ }
+
+ /**
+ * Create a StringBody from the specified text and character set.
+ * The mime type is set to "text/plain".
+ *
+ * @param text to be used for the body, not {@code null}
+ * @param charset the character set, may be {@code null}, in which case the US-ASCII charset is used
+ * @throws UnsupportedEncodingException
+ * @throws IllegalArgumentException if the {@code text} parameter is null
+ */
+ public StringBody(final String text, final Charset charset) throws UnsupportedEncodingException {
+ this(text, "text/plain", charset);
+ }
+
+ /**
+ * Create a StringBody from the specified text.
+ * The mime type is set to "text/plain".
+ * The hosts default charset is used.
+ *
+ * @param text to be used for the body, not {@code null}
+ * @throws UnsupportedEncodingException
+ * @throws IllegalArgumentException if the {@code text} parameter is null
+ */
+ public StringBody(final String text) throws UnsupportedEncodingException {
+ this(text, "text/plain", null);
+ }
+
+ public Reader getReader() {
+ return new InputStreamReader(
+ new ByteArrayInputStream(this.content),
+ this.charset);
+ }
+
+ public void writeTo(final OutputStream out) throws IOException {
+ if (out == null) {
+ throw new IllegalArgumentException("Output stream may not be null");
+ }
+ InputStream in = new ByteArrayInputStream(this.content);
+ byte[] tmp = new byte[4096];
+ int l;
+ while ((l = in.read(tmp)) != -1) {
+ out.write(tmp, 0, l);
+ }
+ out.flush();
+ }
+
+ public String getTransferEncoding() {
+ return MIME.ENC_8BIT;
+ }
+
+ public String getCharset() {
+ return this.charset.name();
+ }
+
+ public long getContentLength() {
+ return this.content.length;
+ }
+
+ public String getFilename() {
+ return null;
+ }
+
+}
diff --git a/Android/OcrApiService/org/apache/http/entity/mime/content/package.html b/Android/OcrApiService/org/apache/http/entity/mime/content/package.html
new file mode 100755
index 0000000..4532091
--- /dev/null
+++ b/Android/OcrApiService/org/apache/http/entity/mime/content/package.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+MIME Multipart content body parts.
+
+
diff --git a/Android/OcrApiService/org/apache/http/entity/mime/package.html b/Android/OcrApiService/org/apache/http/entity/mime/package.html
new file mode 100755
index 0000000..1472f3f
--- /dev/null
+++ b/Android/OcrApiService/org/apache/http/entity/mime/package.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+Support for MIME multipart encoded entities
+
+
diff --git a/Android/OcrApiService/readme.md b/Android/OcrApiService/readme.md
new file mode 100755
index 0000000..f35aab6
--- /dev/null
+++ b/Android/OcrApiService/readme.md
@@ -0,0 +1,98 @@
+OcrApiService.com plugin for Cordova
+---------------
+This plug-in exposes OcrApiService.com optical character recognition api.
+Tested with Cordova 2.0
+
+## Integration instruction ##
+
+* Sign up for an account at http://ocrapiservice.com to get an api key
+* Copy in your "src" folder :
+ * the "com" folder
+ * the "org" folder
+* Add "ocrapiservice.js" to your www folder
+ * include it in your index.html file `
+`
+* Open your config.xml and add ``
+
+## Usage functions ##
+
+### ocrapiservice.convert ###
+Returns a text from an image
+
+**ocrapiservice.convert ( success, error, imageURI, languageCode, apiKey );**
+
+**Parameters**
+
+* success : The callback that is called with the extracted text.
+* error : The callback that is called if there was an error.
+* imageURI : A URI containing the image.
+* languageCode : Two letters language code
+* apiKey : Private api key provided in your ocrapiservice.com dashboard
+
+### Quick example ####
+
+
+### MIT License ###
+
+Copyright (c) 2012 Smart Mobile Software
+
+ 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.
diff --git a/Android/PayPalPlugin/AndroidManifest.xml b/Android/PayPalPlugin/AndroidManifest.xml
index 9ed944d..00d406a 100644
--- a/Android/PayPalPlugin/AndroidManifest.xml
+++ b/Android/PayPalPlugin/AndroidManifest.xml
@@ -1,28 +1,41 @@
-
-
-
+
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Android/PayPalPlugin/LICENSE b/Android/PayPalPlugin/LICENSE
index 4a77837..3925c9a 100644
--- a/Android/PayPalPlugin/LICENSE
+++ b/Android/PayPalPlugin/LICENSE
@@ -3,6 +3,7 @@ The MIT License
Copyright (C) 2011 Mobile Developer Solutions
Copyright (C) 2011, Appception, Inc.
+Copyright (C) 2012, Bucka IT, Tomaz Kregar s.p.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/Android/PayPalPlugin/README.md b/Android/PayPalPlugin/README.md
index 72ddb62..be134a2 100644
--- a/Android/PayPalPlugin/README.md
+++ b/Android/PayPalPlugin/README.md
@@ -1,19 +1,20 @@
# PhoneGap PayPal-Plugin #
-by Paul Beusterien, Mobile Developer Solutions and Carl Stehle, Appception Inc.
+by Paul Beusterien, Mobile Developer Solutions, Carl Stehle, Appception Inc. and Tomaz Kregar, Bucka IT
## Adding the Plugin to your project ##
-Using this plugin requires Android PhoneGap and the PayPal Mobile Payments Library. The PayPal Mobile Payments Library can be downloaded [here](https://www.x.com/community/ppx/xspaces/mobile/mep).
+Using this plugin requires Android Cordova (PhoneGap) and the PayPal Mobile Payments Library. The PayPal Mobile Payments Library can be downloaded [here](https://www.x.com/community/ppx/xspaces/mobile/mep).
-1. Create an Android PhoneGap project. Details at http://www.mobiledevelopersolutions.com/home/start
+1. Create an Android Cordova project. Details at http://docs.phonegap.com/en/2.0.0/guide_getting-started_android_index.md.html
2. Put PayPal_MPL.jar into your project's libs directory and add it to the build path. In Eclipse, right click on PayPal_MPL.jar and select Add to Build Path.
3. Copy assets/www/ files into your project's assets/www/ directory
4. Copy src/com/phonegap/plugin/ files into your project's src/com/phonegap/plugin/ directory
5. Make sure your AndroidManifest.xml includes a superset of the permissions shown in the reference AndroidManifest.xml
6. Add the com.paypal.android.MEP.PayPalActivity as shown in the reference AndroidManifest.xml
-7. Make sure the phonegap.{version}.js filename in index.html matches the filename in your www directory.
-8. Deploy and test the app. The default environment is ENV_NONE.
+7. Include the plugin registration in /res/xml/config.xml as shown in the reference config.xml
+8. Make sure the cordova.{version}.js filename in index.html matches the filename in your www directory.
+9. Deploy and test the app. The default environment is ENV_NONE.
## Using the PayPal Sandbox ##
@@ -24,7 +25,10 @@ Using this plugin requires Android PhoneGap and the PayPal Mobile Payments Libra
## RELEASE NOTES ##
-### 201100618 ###
+### 20120829 ###
+* Added suppport for Cordova 2.0.0
+
+### 20110618 ###
* Initial release
* By default the PayPalPlugin-Host runs in ENV_NONE (offline) with a dummy PayPal ID. Change to ENV_SANDBOX or ENV_LIVE
* Only tested with ENV_NONE and ENV_SANDBOX
diff --git a/Android/PayPalPlugin/assets/www/index.html b/Android/PayPalPlugin/assets/www/index.html
index e7a51b7..08290dc 100644
--- a/Android/PayPalPlugin/assets/www/index.html
+++ b/Android/PayPalPlugin/assets/www/index.html
@@ -1,7 +1,7 @@
-
+
diff --git a/Android/PayPalPlugin/assets/www/paypal.js b/Android/PayPalPlugin/assets/www/paypal.js
index ef6367d..da333ee 100644
--- a/Android/PayPalPlugin/assets/www/paypal.js
+++ b/Android/PayPalPlugin/assets/www/paypal.js
@@ -2,13 +2,13 @@
*
* Copyright (C) 2011, Appception, Inc.. All Rights Reserved.
* Copyright (C) 2011, Mobile Developer Solutions All Rights Reserved.
+* Copyright (C) 2012, Bucka IT, Tomaz Kregar s.p. All Rights Reserved.
*/
/*
* @return Instance of PayPal
*/
-var PayPal = function() {
-};
+var PayPal = function() {};
/**
* @param directory
@@ -29,7 +29,7 @@ var fail = function(e) {
PayPal.prototype.invoke = function(callee, arg, successFunction) {
var succ = successFunction ? successFunction : genericSuccess;
- return PhoneGap.exec(succ,
+ return cordova.exec(succ,
fail,
'PayPalPlugin', // Telling PhoneGap that we want to run "PayPal Plugin"
callee, // Telling the plugin, which action we want to perform
@@ -38,34 +38,40 @@ PayPal.prototype.invoke = function(callee, arg, successFunction) {
/**
*
- *
Register the Directory Listing Javascript plugin.
- *
Also register native call which will be called when this plugin runs
+ *
Register native call which will be called when this plugin runs
+ *
Plugin must be registered in /res/xml/config.xml : <plugin name="PayPalPlugin" value="com.phonegap.plugin.PayPalPlugin"/>
*
*/
-PhoneGap.addConstructor(function() {
- // Register the javascript plugin with PhoneGap
- PhoneGap.addPlugin('PayPal', new PayPal());
- // Register the native class of plugin with PhoneGap
- PluginManager.addService("PayPalPlugin", "com.phonegap.plugin.PayPalPlugin");
-});
+if(cordova.addPlugin) { //versions before 2.0.0
+ cordova.addConstructor(function() {
+ // Register the javascript plugin with PhoneGap
+ cordova.addPlugin('PayPal', new PayPal());
+ });
+} else {
+ // Creating the native object of plugin
+ if(!window.plugins)
+ window.plugins = {};
+ if (!window.plugins.PayPal)
+ window.plugins.PayPal = new PayPal();
+}
var mpl = {
- construct : function(str) {
- window.plugins.PayPal.invoke('construct', str);
- },
- prepare : function(ptype) {
- window.plugins.PayPal.invoke('prepare', ptype);
- },
- getStatus : function() {
- return window.plugins.PayPal.invoke('getStatus', null, getStatusCallback);
- },
- setPaymentInfo : function(arg) {
- window.plugins.PayPal.invoke('setPaymentInfo', arg);
- },
- payButton : function(btype) {
- window.plugins.PayPal.invoke('pay', btype);
- }
+ construct : function(str) {
+ window.plugins.PayPal.invoke('construct', str);
+ },
+ prepare : function(ptype) {
+ window.plugins.PayPal.invoke('prepare', ptype);
+ },
+ getStatus : function() {
+ return window.plugins.PayPal.invoke('getStatus', null, getStatusCallback);
+ },
+ setPaymentInfo : function(arg) {
+ window.plugins.PayPal.invoke('setPaymentInfo', arg);
+ },
+ payButton : function(btype) {
+ window.plugins.PayPal.invoke('pay', btype);
+ }
};
/*
diff --git a/Android/PayPalPlugin/res/xml/config.xml b/Android/PayPalPlugin/res/xml/config.xml
new file mode 100644
index 0000000..61b91bb
--- /dev/null
+++ b/Android/PayPalPlugin/res/xml/config.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Android/PayPalPlugin/src/com/phonegap/plugin/PayPalPlugin.java b/Android/PayPalPlugin/src/com/phonegap/plugin/PayPalPlugin.java
index 2b5e100..6a7cf97 100644
--- a/Android/PayPalPlugin/src/com/phonegap/plugin/PayPalPlugin.java
+++ b/Android/PayPalPlugin/src/com/phonegap/plugin/PayPalPlugin.java
@@ -2,6 +2,7 @@
*
* Copyright (C) 2011, Appception, Inc.. All Rights Reserved.
* Copyright (C) 2011, Mobile Developer Solutions All Rights Reserved.
+ * Copyright (C) 2012, Bucka IT, Tomaz Kregar s.p. All Rights Reserved.
*/
package com.phonegap.plugin;
@@ -12,9 +13,9 @@ import org.json.JSONObject;
import android.util.Log;
-import com.phonegap.api.Plugin;
-import com.phonegap.api.PluginResult;
-import com.phonegap.api.PluginResult.Status;
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
+import org.apache.cordova.api.PluginResult.Status;
public class PayPalPlugin extends Plugin {
private static mpl PluginMpl;
@@ -29,7 +30,7 @@ public class PayPalPlugin extends Plugin {
try {
if (action.equals("construct")) {
thisPlugin = this;
- PluginMpl = new mpl(this.ctx, data.getString(0));
+ PluginMpl = new mpl(this.ctx.getContext(), data.getString(0));
// PluginMpl = new mpl(this.ctx, PayPal.ENV_NONE, "");
result = new PluginResult(Status.OK);
diff --git a/Android/PayPalPlugin/src/com/phonegap/plugin/mpl.java b/Android/PayPalPlugin/src/com/phonegap/plugin/mpl.java
index 4293253..f59db1c 100644
--- a/Android/PayPalPlugin/src/com/phonegap/plugin/mpl.java
+++ b/Android/PayPalPlugin/src/com/phonegap/plugin/mpl.java
@@ -2,6 +2,7 @@
*
* Copyright (C) 2011, Appception, Inc.. All Rights Reserved.
* Copyright (C) 2011, Mobile Developer Solutions All Rights Reserved.
+ * Copyright (C) 2012, Bucka IT, Tomaz Kregar s.p. All Rights Reserved.
*/
package com.phonegap.plugin;
@@ -23,7 +24,7 @@ import com.paypal.android.MEP.PayPalInvoiceData;
import com.paypal.android.MEP.PayPalInvoiceItem;
import com.paypal.android.MEP.PayPalPayment;
import com.paypal.android.MEP.PayPalPreapproval;
-import com.phonegap.DroidGap;
+import org.apache.cordova.DroidGap;
public class mpl {
@@ -88,9 +89,9 @@ public class mpl {
}
// Construct new instance with information from main activity
- public mpl(Context context, String JSONdata) {
- mpl_activity = (Activity) context;
- mpl_context = context.getApplicationContext();
+ public mpl(Context ctx, String JSONdata) {
+ mpl_activity = (Activity) ctx;
+ mpl_context = ctx.getApplicationContext();
parseConstructArg(JSONdata);
if (mpjs_server.equals("ENV_NONE")) {
server = PayPal.ENV_NONE;
diff --git a/Android/SMSPlugin/src/net/practicaldeveloper/phonegap/plugins/SmsPlugin.java b/Android/SMSPlugin/src/net/practicaldeveloper/phonegap/plugins/SmsPlugin.java
index c5da050..4d46058 100644
--- a/Android/SMSPlugin/src/net/practicaldeveloper/phonegap/plugins/SmsPlugin.java
+++ b/Android/SMSPlugin/src/net/practicaldeveloper/phonegap/plugins/SmsPlugin.java
@@ -29,8 +29,8 @@ import android.app.PendingIntent;
import android.content.Intent;
import android.telephony.SmsManager;
-import com.phonegap.api.Plugin;
-import com.phonegap.api.PluginResult;
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
public class SmsPlugin extends Plugin {
public final String ACTION_SEND_SMS = "SendSMS";
diff --git a/Android/Screenshot/README.md b/Android/Screenshot/README.md
new file mode 100644
index 0000000..c0ff635
--- /dev/null
+++ b/Android/Screenshot/README.md
@@ -0,0 +1,36 @@
+Screenshot
+===============
+Plugin for Cordova (1.6+)
+
+The Screenshot plugin allows your application to take screenshots of the current screen and save them into the phone.
+
+Platforms
+---------
+Currently available on:
+
+### Android
+Copy the content of *src* folder to your *src* folder.
+
+Copy the content of *www* folder to you *www* folder.
+
+Edit your *AndroidManifest.xml* and add the following permission:
+``
+
+In addition you have to edit your *res/xml/plugins.xml* file to let Cordova know about the plugin:
+``
+
+### iOS
+Copy the *Screenshot.h* and *Screenshot.m* files to your projects "Plugins" folder.
+
+Copy the *Screenshot.js* file to you *www* folder.
+
+Add the Screenshot plugin to the *Cordova.plist* file (to the Plugins list). Both Key and Value are "Screenshot".
+
+
+License
+=======
+Copyright (C) 2012 30ideas (http://30ide.as)
+MIT licensed
+
+Created by Josemando Sobral
+Jul 2nd, 2012
diff --git a/Android/Screenshot/v1.8.1/src/org/apache/cordova/Screenshot.java b/Android/Screenshot/v1.8.1/src/org/apache/cordova/Screenshot.java
new file mode 100644
index 0000000..1b7d1e7
--- /dev/null
+++ b/Android/Screenshot/v1.8.1/src/org/apache/cordova/Screenshot.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (C) 2012 30ideas (http://30ide.as)
+ * MIT licensed
+ *
+ * @author Josemando Sobral
+ * @created Jul 2nd, 2012.
+ */
+package org.apache.cordova;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
+import org.json.JSONArray;
+
+import android.graphics.Bitmap;
+import android.os.Environment;
+import android.view.View;
+
+public class Screenshot extends Plugin {
+ private PluginResult result = null;
+
+ @Override
+ public PluginResult execute(String action, JSONArray args, String callbackId) {
+ // starting on ICS, some WebView methods
+ // can only be called on UI threads
+ super.cordova.getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ View view = webView.getRootView();
+
+ view.setDrawingCacheEnabled(true);
+ Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
+ view.setDrawingCacheEnabled(false);
+
+ try {
+ File folder = new File(Environment.getExternalStorageDirectory(), "Pictures");
+ if (!folder.exists()) {
+ folder.mkdirs();
+ }
+
+ File f = new File(folder, "screenshot_" + System.currentTimeMillis() + ".png");
+
+ FileOutputStream fos = new FileOutputStream(f);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
+ result = new PluginResult(PluginResult.Status.OK);
+
+ } catch (IOException e) {
+ result = new PluginResult(PluginResult.Status.IO_EXCEPTION, e.getMessage());
+ }
+ }
+ });
+
+ // waiting ui thread to finish
+ while (this.result == null) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ // ignoring exception, since we have to wait
+ // ui thread to finish
+ }
+ }
+
+ return this.result;
+ }
+
+}
diff --git a/Android/Screenshot/v1.8.1/www/Screenshot.js b/Android/Screenshot/v1.8.1/www/Screenshot.js
new file mode 100644
index 0000000..e418c42
--- /dev/null
+++ b/Android/Screenshot/v1.8.1/www/Screenshot.js
@@ -0,0 +1,40 @@
+/*
+ * This code is adapted from the work of Michael Nachbaur
+ * by Simon Madine of The Angry Robot Zombie Factory
+ * - Converted to Cordova 1.6.1 by Josemando Sobral.
+ * 2012-07-03
+ * MIT licensed
+ */
+
+/*
+ * Temporary Scope to contain the plugin.
+ * - More information here:
+ * https://github.com/apache/incubator-cordova-ios/blob/master/guides/Cordova%20Plugin%20Upgrade%20Guide.md
+ */
+(function() {
+ /* Get local ref to global PhoneGap/Cordova/cordova object for exec function.
+ - This increases the compatibility of the plugin. */
+ var cordovaRef = window.PhoneGap || window.Cordova || window.cordova; // old to new fallbacks
+
+ /**
+ * This class exposes the ability to take a Screenshot to JavaScript
+ */
+ function Screenshot() { }
+
+ /**
+ * Save the screenshot to the user's Photo Library
+ */
+ Screenshot.prototype.saveScreenshot = function() {
+ cordovaRef.exec(null, null, "Screenshot", "saveScreenshot", []);
+ };
+
+ cordovaRef.addConstructor(function() {
+ if (!window.plugins) {
+ window.plugins = {};
+ }
+ if (!window.plugins.screenshot) {
+ window.plugins.screenshot = new Screenshot();
+ }
+ });
+
+ })(); /* End of Temporary Scope. */
\ No newline at end of file
diff --git a/Android/Screenshot/v2.0.0/src/org/apache/cordova/Screenshot.java b/Android/Screenshot/v2.0.0/src/org/apache/cordova/Screenshot.java
new file mode 100644
index 0000000..651f217
--- /dev/null
+++ b/Android/Screenshot/v2.0.0/src/org/apache/cordova/Screenshot.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (C) 2012 30ideas (http://30ide.as)
+ * MIT licensed
+ *
+ * @author Josemando Sobral
+ * @created Jul 2nd, 2012.
+ */
+package org.apache.cordova;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
+import org.json.JSONArray;
+
+import android.graphics.Bitmap;
+import android.os.Environment;
+import android.view.View;
+
+public class Screenshot extends Plugin {
+
+ @Override
+ public PluginResult execute(String action, JSONArray args, String callbackId) {
+ // starting on ICS, some WebView methods
+ // can only be called on UI threads
+ final Plugin that = this;
+ final String id = callbackId;
+ super.cordova.getActivity().runOnUiThread(new Runnable() {
+ //@Override
+ public void run() {
+ View view = webView.getRootView();
+
+ view.setDrawingCacheEnabled(true);
+ Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
+ view.setDrawingCacheEnabled(false);
+
+ try {
+ File folder = new File(Environment.getExternalStorageDirectory(), "Pictures");
+ if (!folder.exists()) {
+ folder.mkdirs();
+ }
+
+ File f = new File(folder, "screenshot_" + System.currentTimeMillis() + ".png");
+
+ FileOutputStream fos = new FileOutputStream(f);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
+ that.success(new PluginResult(PluginResult.Status.OK), id);
+
+ } catch (IOException e) {
+ that.success(new PluginResult(PluginResult.Status.IO_EXCEPTION, e.getMessage()), id);
+ }
+ }
+ });
+
+ PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
+ result.setKeepCallback(true);
+ return result;
+ }
+
+}
diff --git a/Android/Screenshot/v2.0.0/www/Screenshot.js b/Android/Screenshot/v2.0.0/www/Screenshot.js
new file mode 100644
index 0000000..7d07d9c
--- /dev/null
+++ b/Android/Screenshot/v2.0.0/www/Screenshot.js
@@ -0,0 +1,35 @@
+/*
+ * This code is adapted from the work of Michael Nachbaur
+ * by Simon Madine of The Angry Robot Zombie Factory
+ * - Converted to Cordova 1.6.1 by Josemando Sobral.
+ * - Converted to Cordova 2.0.0 by Simon MacDonald
+ * 2012-07-03
+ * MIT licensed
+ */
+
+cordova.define("cordova/plugin/screenshot", function(require, exports, module) {
+ var exec = require('cordova/exec');
+
+ /**
+ * This class exposes the ability to take a Screenshot to JavaScript
+ */
+ var Screenshot = function() {};
+
+ /**
+ * Save the screenshot to the user's Photo Library
+ */
+ Screenshot.prototype.saveScreenshot = function() {
+ exec(null, null, "Screenshot", "saveScreenshot", []);
+ };
+
+ var screenshot = new Screenshot();
+ module.exports = screenshot;
+
+});
+
+if (!window.plugins) {
+ window.plugins = {};
+}
+if (!window.plugins.screenshot) {
+ window.plugins.screenshot = cordova.require("cordova/plugin/screenshot");
+}
diff --git a/Android/Share/Share.java b/Android/Share/Share.java
index c879feb..b60c33e 100644
--- a/Android/Share/Share.java
+++ b/Android/Share/Share.java
@@ -34,7 +34,7 @@ public class Share extends Plugin {
sendIntent.setType("text/plain");
sendIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
sendIntent.putExtra(android.content.Intent.EXTRA_TEXT, text);
- this.ctx.startActivity(sendIntent);
+ this.cordova.startActivityForResult(this, sendIntent, 0);
}
}
diff --git a/Android/Share/share.js b/Android/Share/share.js
index 71e0727..843268c 100644
--- a/Android/Share/share.js
+++ b/Android/Share/share.js
@@ -15,6 +15,9 @@ Share.prototype.show = function(content, success, fail) {
}, 'Share', '', [content]);
};
-cordova.addConstructor(function(){
- cordova.addPlugin('share', new Share());
-});
\ No newline at end of file
+if(!window.plugins) {
+ window.plugins = {};
+}
+if (!window.plugins.share) {
+ window.plugins.share = new Share();
+}
diff --git a/Android/SpeechRecognizer/SpeechRecognizer.java b/Android/SpeechRecognizer/v1.8.1/SpeechRecognizer.java
similarity index 100%
rename from Android/SpeechRecognizer/SpeechRecognizer.java
rename to Android/SpeechRecognizer/v1.8.1/SpeechRecognizer.java
diff --git a/Android/SpeechRecognizer/SpeechRecognizer.js b/Android/SpeechRecognizer/v1.8.1/SpeechRecognizer.js
similarity index 100%
rename from Android/SpeechRecognizer/SpeechRecognizer.js
rename to Android/SpeechRecognizer/v1.8.1/SpeechRecognizer.js
diff --git a/Android/SpeechRecognizer/v2.0.0/SpeechRecognizer.java b/Android/SpeechRecognizer/v2.0.0/SpeechRecognizer.java
new file mode 100644
index 0000000..6d14c6d
--- /dev/null
+++ b/Android/SpeechRecognizer/v2.0.0/SpeechRecognizer.java
@@ -0,0 +1,199 @@
+/**
+ * SpeechRecognizer.java
+ * Speech Recognition PhoneGap plugin (Android)
+ *
+ * @author Colin Turner
+ *
+ * Copyright (c) 2011, Colin Turner
+ *
+ * MIT Licensed
+ */
+package com.urbtek.phonegap;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
+import org.apache.cordova.api.PluginResult.Status;
+
+import android.util.Log;
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.speech.RecognizerIntent;
+
+/**
+ * Style and such borrowed from the TTS and PhoneListener plugins
+ */
+public class SpeechRecognizer extends Plugin {
+ private static final String LOG_TAG = SpeechRecognizer.class.getSimpleName();
+ public static final String ACTION_INIT = "init";
+ public static final String ACTION_SPEECH_RECOGNIZE = "startRecognize";
+ public static final String NOT_PRESENT_MESSAGE = "Speech recognition is not present or enabled";
+
+ private String speechRecognizerCallbackId = "";
+ private boolean recognizerPresent = false;
+
+ /* (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) {
+ // Dispatcher
+ if (ACTION_INIT.equals(action)) {
+ // init
+ if (DoInit())
+ return new PluginResult(Status.OK);
+ else
+ return new PluginResult(Status.ERROR, NOT_PRESENT_MESSAGE);
+ }
+ else if (ACTION_SPEECH_RECOGNIZE.equals(action)) {
+ // recognize speech
+ if (!recognizerPresent) {
+ return new PluginResult(PluginResult.Status.ERROR, NOT_PRESENT_MESSAGE);
+ }
+ if (!this.speechRecognizerCallbackId.equals("")) {
+ return new PluginResult(PluginResult.Status.ERROR, "Speech recognition is in progress.");
+ }
+
+ this.speechRecognizerCallbackId = callbackId;
+ startSpeechRecognitionActivity(args);
+ PluginResult res = new PluginResult(Status.NO_RESULT);
+ res.setKeepCallback(true);
+ return res;
+ }
+ else {
+ // Invalid action
+ String res = "Unknown action: " + action;
+ return new PluginResult(PluginResult.Status.INVALID_ACTION, res);
+ }
+ }
+
+ /**
+ * Initialize the speech recognizer by checking if one exists.
+ */
+ private boolean DoInit() {
+ this.recognizerPresent = IsSpeechRecognizerPresent();
+ return this.recognizerPresent;
+ }
+
+ /**
+ * Checks if a recognizer is present on this device
+ */
+ private boolean IsSpeechRecognizerPresent() {
+ PackageManager pm = cordova.getActivity().getPackageManager();
+ List activities = pm.queryIntentActivities(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
+ return !activities.isEmpty();
+ }
+
+ /**
+ * Fire an intent to start the speech recognition activity.
+ *
+ * @param args Argument array with the following string args: [req code][number of matches][prompt string]
+ */
+ private void startSpeechRecognitionActivity(JSONArray args) {
+ int reqCode = 42; //Hitchhiker?
+ int maxMatches = 0;
+ String prompt = "";
+
+ try {
+ if (args.length() > 0) {
+ // Request code - passed back to the caller on a successful operation
+ String temp = args.getString(0);
+ reqCode = Integer.parseInt(temp);
+ }
+ if (args.length() > 1) {
+ // Maximum number of matches, 0 means the recognizer decides
+ String temp = args.getString(1);
+ maxMatches = Integer.parseInt(temp);
+ }
+ if (args.length() > 2) {
+ // Optional text prompt
+ prompt = args.getString(2);
+ }
+ }
+ catch (Exception e) {
+ Log.e(LOG_TAG, String.format("startSpeechRecognitionActivity exception: %s", e.toString()));
+ }
+
+ Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
+ intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
+ intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.ENGLISH);
+ if (maxMatches > 0)
+ intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxMatches);
+ if (!prompt.equals(""))
+ intent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
+ cordova.startActivityForResult(this, intent, reqCode);
+ }
+
+ /**
+ * Handle the results from the recognition activity.
+ */
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode == Activity.RESULT_OK) {
+ // Fill the list view with the strings the recognizer thought it could have heard
+ ArrayList matches = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
+ float[] confidence = data.getFloatArrayExtra(RecognizerIntent.EXTRA_CONFIDENCE_SCORES);
+
+ if (confidence != null) {
+ Log.d(LOG_TAG, "confidence length "+ confidence.length);
+ Iterator iterator = matches.iterator();
+ int i = 0;
+ while(iterator.hasNext()) {
+ Log.d(LOG_TAG, "Match = " + iterator.next() + " confidence = " + confidence[i]);
+ i++;
+ }
+ } else {
+ Log.d(LOG_TAG, "No confidence" +
+ "");
+ }
+
+ ReturnSpeechResults(requestCode, matches);
+ }
+ else {
+ // Failure - Let the caller know
+ ReturnSpeechFailure(resultCode);
+ }
+
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
+ private void ReturnSpeechResults(int requestCode, ArrayList matches) {
+ boolean firstValue = true;
+ StringBuilder sb = new StringBuilder();
+ sb.append("{\"speechMatches\": {");
+ sb.append("\"requestCode\": ");
+ sb.append(Integer.toString(requestCode));
+ sb.append(", \"speechMatch\": [");
+
+ Iterator iterator = matches.iterator();
+ while(iterator.hasNext()) {
+ String match = iterator.next();
+
+ if (firstValue == false)
+ sb.append(", ");
+ firstValue = false;
+ sb.append(JSONObject.quote(match));
+ }
+ sb.append("]}}");
+
+ PluginResult result = new PluginResult(PluginResult.Status.OK, sb.toString());
+ result.setKeepCallback(false);
+ this.success(result, this.speechRecognizerCallbackId);
+ this.speechRecognizerCallbackId = "";
+ }
+
+ private void ReturnSpeechFailure(int resultCode) {
+ PluginResult result = new PluginResult(PluginResult.Status.ERROR, Integer.toString(resultCode));
+ result.setKeepCallback(false);
+ this.error(result, this.speechRecognizerCallbackId);
+ this.speechRecognizerCallbackId = "";
+ }
+}
\ No newline at end of file
diff --git a/Android/SpeechRecognizer/v2.0.0/SpeechRecognizer.js b/Android/SpeechRecognizer/v2.0.0/SpeechRecognizer.js
new file mode 100644
index 0000000..809ff65
--- /dev/null
+++ b/Android/SpeechRecognizer/v2.0.0/SpeechRecognizer.js
@@ -0,0 +1,60 @@
+/**
+ * SpeechRecognizer.js
+ * Speech Recognizer cordova plugin (Android)
+ *
+ * @author Colin Turner
+ *
+ * MIT Licensed
+ */
+
+/**
+ * c'tor
+ */
+function SpeechRecognizer() {
+}
+
+/**
+ * Initialize
+ *
+ * @param successCallback
+ * @param errorCallback
+ */
+SpeechRecognizer.prototype.init = function(successCallback, errorCallback) {
+ return cordova.exec(successCallback, errorCallback, "SpeechRecognizer", "init", []);
+};
+
+/**
+ * Recognize speech and return a list of matches
+ *
+ * @param successCallback
+ * @param errorCallback
+ * @param reqCode User-defined integer request code which will be returned when recognition is complete
+ * @param maxMatches The maximum number of matches to return. 0 means the service decides how many to return.
+ * @param promptString An optional string to prompt the user during recognition
+ */
+SpeechRecognizer.prototype.startRecognize = function(successCallback, errorCallback, reqCode, maxMatches, promptString) {
+ return cordova.exec(successCallback, errorCallback, "SpeechRecognizer", "startRecognize", [reqCode, maxMatches, promptString]);
+};
+
+/**
+ * Get the list of the supported languages in IETF BCP 47 format
+ *
+ * @param successCallback
+ * @param errorCallback
+ *
+ * Returns an array of codes in the success callback
+ */
+SpeechRecognizer.prototype.getSupportedLanguages = function(successCallback, errorCallback) {
+ return cordova.exec(successCallback, errorCallback, "SpeechRecognizer", "getSupportedLanguages", []);
+};
+
+/**
+ * Load
+ */
+if(!window.plugins) {
+ window.plugins = {};
+}
+if (!window.plugins.speechrecognizer) {
+ window.plugins.speechrecognizer = new SpeechRecognizer();
+}
+
diff --git a/Android/StatusBarNotification/README.md b/Android/StatusBarNotification/README.md
index c15a31c..2731567 100644
--- a/Android/StatusBarNotification/README.md
+++ b/Android/StatusBarNotification/README.md
@@ -1,51 +1,35 @@
-# StatusBarNotification plugin for Phonegap #
+# StatusBarNotification plugin for Cordova #
-Plugin allows you to display notifications in the status bar from your PhoneGap application.
+This plugin allows you to display notifications in the status bar from your Cordova application. On Android you have to explicitly add things to the status bar (as opposed to iOS where push notifications automatically get displayed in the UI). The Android status bar is the UI component at the top of the screen that has a bunch of little icons. You can also drag the status bar down to view a list of notifications.
## Adding the Plugin to your project ##
-Using this plugin requires [Android PhoneGap](http://github.com/phonegap/phonegap-android).
+Using this plugin requires [Android Cordova](http://github.com/apache/incubator-cordova-android).
-1. To install the plugin, move statusbarnotification.js to your project's www folder and include a reference to it in your html file after phonegap.js.
+1. To install the plugin, move statusbarnotification.js to your project's www folder and include a reference to it in your html file after cordova.js.
- <script type="text/javascript" charset="utf-8" src="phonegap.js"></script>
+ <script type="text/javascript" charset="utf-8" src="cordova.js"></script>
<script type="text/javascript" charset="utf-8" src="statusbarnotification.js"></script>
-2. Create a directory within your project called "src/com/phonegap/plugins/statusBarNotification" and move StatusBarNotification.java into it.
+2. Create a directory within your project called "src/com/phonegap/plugins/statusBarNotification" and move the .java files from this folder into it.
-3. Add the following activity to your AndroidManifest.xml file. It should be added inside the <application> tag.
-
- <activity android:name="com.phonegap.DroidGap" android:label="@string/app_name">
- <intent-filter>
- </intent-filter>
- </activity>
-
-4. In your res/xml/plugins.xml file add the following line:
+3. In your res/xml/plugins.xml file add the following line:
<plugin name="StatusBarNotification" value="com.phonegap.plugins.statusBarNotification.StatusBarNotification"/>
-5. You will need to add a nofication.png file to you applications res/drawable-ldpi, res/drawable-mdpi & res/drawable-hdpi directories.
+ CAUTION: Using PhoneGap ≥ 2.0 (aka Cordova) you have to add this line into res/xml/config.xml in the <plugins>-section.
+The plugins.xml is no longer supported. The plugins are all located in the config.xml
-6. You will need to add an import line like:
+
+4. You will need to add a notification.png file to your applications res/drawable-ldpi, res/drawable-mdpi & res/drawable-hdpi or res/drawable-xhdpi directories (depending on what resolutions you want to support).
+
+5. You will need to add an import line like this to the .java files (see commented out lines inside the files):
import com.my.app.R;
-Where com.my.app is your applications package name in order for this to compile.
-
## Using the plugin ##
-The plugin creates the object `window.plugins.statusBarNotification`. To use, call one of the following, available methods:
-
-
- /**
- * Displays new status bar notification
- *
- * @param notificationTitle The url to load
- * @param notificationBody Load url in PhoneGap webview [optional] - Default: false
- */
-
- notify(notificationTitle, notificationBody);
-
+The plugin creates the object `window.plugins.statusBarNotification`.
Sample use:
@@ -54,6 +38,7 @@ Sample use:
## License ##
Copyright (C) 2011 Dmitry Savchenko
+Copyright (C) 2012 Max Ogden
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
diff --git a/Android/StatusBarNotification/StatusBarNotification.java b/Android/StatusBarNotification/StatusBarNotification.java
index 8b523dd..74c58b9 100644
--- a/Android/StatusBarNotification/StatusBarNotification.java
+++ b/Android/StatusBarNotification/StatusBarNotification.java
@@ -1,116 +1,138 @@
/*
-*
-* Copyright (C) 2011 Dmitry Savchenko
-*
-* 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.
-*
-*/
+ *
+ * Copyright (C) 2011 Dmitry Savchenko
+ *
+ * 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.
+ *
+ */
package com.phonegap.plugins.statusBarNotification;
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
+import org.apache.cordova.api.PluginResult.Status;
import org.json.JSONArray;
import org.json.JSONException;
import android.app.Notification;
import android.app.NotificationManager;
-import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+
+import android.os.Handler;
import android.util.Log;
-import com.phonegap.api.Plugin;
-import com.phonegap.api.PluginResult;
-import com.phonegap.api.PluginResult.Status;
-
public class StatusBarNotification extends Plugin {
- // Action to execute
- public static final String NOTIFY = "notify";
- public static final String CLEAR = "clear";
-
- /**
- * Executes the request and returns PluginResult
- *
- * @param action Action to execute
- * @param data JSONArray of arguments to the plugin
- * @param callbackId The callback id used when calling back into JavaScript
- *
- * @return A PluginRequest object with a status
- * */
- @Override
- public PluginResult execute(String action, JSONArray data, String callbackId) {
- String ns = Context.NOTIFICATION_SERVICE;
- mNotificationManager = (NotificationManager) ctx.getSystemService(ns);
- context = ctx.getApplicationContext();
-
- PluginResult result = null;
- if (NOTIFY.equals(action)) {
- try {
+ // Action to execute
+ public static final String NOTIFY = "notify";
+ public static final String CLEAR = "clear";
- String title = data.getString(0);
- String body = data.getString(1);
- Log.d("NotificationPlugin", "Notification: " + title + ", " + body);
- showNotification(title, body);
- result = new PluginResult(Status.OK);
- } catch (JSONException jsonEx) {
- Log.d("NotificationPlugin", "Got JSON Exception "
- + jsonEx.getMessage());
- result = new PluginResult(Status.JSON_EXCEPTION);
- }
- } else if (CLEAR.equals(action)){
- clearNotification();
- } else {
- result = new PluginResult(Status.INVALID_ACTION);
- Log.d("NotificationPlugin", "Invalid action : "+action+" passed");
- }
- return result;
- }
+ /**
+ * Executes the request and returns PluginResult
+ *
+ * @param action Action to execute
+ * @param data JSONArray of arguments to the plugin
+ * @param callbackId The callback id used when calling back into JavaScript
+ *
+ * @return A PluginRequest object with a status
+ * */
+ @Override
+ public PluginResult execute(String action, JSONArray data, String callbackId) {
+ PluginResult result = null;
+ if (NOTIFY.equals(action)) {
+ try {
+ String tag = data.getString(0);
+ String title = data.getString(1);
+ String body = data.getString(2);
+ Log.d("NotificationPlugin", "Notification: " + tag + ", " + title + ", " + body);
+ showNotification(tag, title, body);
+ result = new PluginResult(Status.OK);
+ } catch (JSONException jsonEx) {
+ Log.d("NotificationPlugin", "Got JSON Exception "
+ + jsonEx.getMessage());
+ result = new PluginResult(Status.JSON_EXCEPTION);
+ }
+ } else if (CLEAR.equals(action)){
+ try {
+ String tag = data.getString(0);
+ Log.d("NotificationPlugin", "Notification cancel: " + tag);
+ clearNotification(tag);
+ } catch (JSONException jsonEx) {
+ Log.d("NotificationPlugin", "Got JSON Exception " + jsonEx.getMessage());
+ result = new PluginResult(Status.JSON_EXCEPTION);
+ }
+ } else {
+ result = new PluginResult(Status.INVALID_ACTION);
+ Log.d("NotificationPlugin", "Invalid action : "+action+" passed");
+ }
+ return result;
+ }
- /**
- * Displays status bar notification
- *
- * @param contentTitle Notification title
- * @param contentText Notification text
- * */
- public void showNotification( CharSequence contentTitle, CharSequence contentText ) {
- int icon = R.drawable.notification;
- long when = System.currentTimeMillis();
-
- Notification notification = new Notification(icon, contentTitle, when);
- //notification.flags |= Notification.FLAG_NO_CLEAR; //Notification cannot be clearned by user
-
- Intent notificationIntent = new Intent(ctx, ctx.getClass());
- PendingIntent contentIntent = PendingIntent.getActivity(ctx, 0, notificationIntent, 0);
- notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
-
- mNotificationManager.notify(1, notification);
- }
-
- /**
- * Removes the Notification from status bar
- */
- public void clearNotification() {
- mNotificationManager.cancelAll();
- }
-
- private NotificationManager mNotificationManager;
+ /**
+ * Displays status bar notification
+ *
+ * @param tag Notification tag.
+ * @param contentTitle Notification title
+ * @param contentText Notification text
+ * */
+ public void showNotification( CharSequence tag, CharSequence contentTitle, CharSequence contentText ) {
+ String ns = Context.NOTIFICATION_SERVICE;
+ context = cordova.getActivity().getApplicationContext();
+ mNotificationManager = (NotificationManager) context.getSystemService(ns);
+
+ Notification noti = StatusNotificationIntent.buildNotification(context, tag, contentTitle, contentText);
+ mNotificationManager.notify(tag.hashCode(), noti);
+ }
+
+ /**
+ * Cancels a single notification by tag.
+ *
+ * @param tag Notification tag to cancel.
+ */
+ public void clearNotification(String tag) {
+ mNotificationManager.cancel(tag.hashCode());
+ }
+
+ /**
+ * Removes all Notifications from the status bar.
+ */
+ public void clearAllNotifications() {
+ mNotificationManager.cancelAll();
+ }
+
+ /**
+ * Called when a notification is clicked.
+ * @param intent The new Intent passed from the notification.
+ */
+ @Override
+ public void onNewIntent(Intent intent) {
+ // The incoming Intent may or may not have been for a notification.
+ String tag = intent.getStringExtra("notificationTag");
+ if (tag != null) {
+ sendJavascript("window.Notification.callOnclickByTag('"+ tag + "')");
+ }
+ }
+
+
+ private NotificationManager mNotificationManager;
private Context context;
}
diff --git a/Android/StatusBarNotification/StatusNotificationIntent.java b/Android/StatusBarNotification/StatusNotificationIntent.java
new file mode 100644
index 0000000..d5314fb
--- /dev/null
+++ b/Android/StatusBarNotification/StatusNotificationIntent.java
@@ -0,0 +1,27 @@
+// This class is used on all Androids below Honeycomb
+package com.phonegap.plugins.statusBarNotification;
+// import com.yourapp.R;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+
+public class StatusNotificationIntent {
+ public static Notification buildNotification( Context context, CharSequence tag, CharSequence contentTitle, CharSequence contentText ) {
+ int icon = R.drawable.notification;
+ long when = System.currentTimeMillis();
+ Notification noti = new Notification(icon, contentTitle, when);
+ noti.flags |= Notification.FLAG_AUTO_CANCEL;
+
+ PackageManager pm = context.getPackageManager();
+ Intent notificationIntent = pm.getLaunchIntentForPackage(context.getPackageName());
+ notificationIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ notificationIntent.putExtra("notificationTag", tag);
+
+ PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
+ noti.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
+ return noti;
+ }
+}
diff --git a/Android/StatusBarNotification/drawable-hdpi/notification.png b/Android/StatusBarNotification/drawable-hdpi/notification.png
new file mode 100644
index 0000000..00e8f8a
Binary files /dev/null and b/Android/StatusBarNotification/drawable-hdpi/notification.png differ
diff --git a/Android/StatusBarNotification/drawable-mdpi/notification.png b/Android/StatusBarNotification/drawable-mdpi/notification.png
new file mode 100644
index 0000000..b41ccd0
Binary files /dev/null and b/Android/StatusBarNotification/drawable-mdpi/notification.png differ
diff --git a/Android/StatusBarNotification/drawable-xhdpi/notification.png b/Android/StatusBarNotification/drawable-xhdpi/notification.png
new file mode 100644
index 0000000..15182b9
Binary files /dev/null and b/Android/StatusBarNotification/drawable-xhdpi/notification.png differ
diff --git a/Android/StatusBarNotification/statusbarnotification.js b/Android/StatusBarNotification/statusbarnotification.js
index ea29f40..4a546ba 100644
--- a/Android/StatusBarNotification/statusbarnotification.js
+++ b/Android/StatusBarNotification/statusbarnotification.js
@@ -25,36 +25,111 @@
*
*/
-/**
- *
- * Constructor
- */
-var NotificationMessenger = function() {
-}
+var cordovaRef = window.PhoneGap || window.Cordova || window.cordova; // old to new fallbacks
+
+/** @deprecated Use the W3C standard window.Notification API instead. */
+var NotificationMessenger = function() { }
/**
* @param title Title of the notification
* @param body Body of the notification
+ * @deprecated Use the W3C standard window.Notification API instead.
*/
NotificationMessenger.prototype.notify = function(title, body) {
- return PhoneGap.exec(null, null, 'StatusBarNotification', 'notify', [title, body]);
-};
+ if (window.Notification) {
+ this.activeNotification = new window.Notification(title, {
+ body: body
+ });
+ }
+}
/**
* Clears the Notificaiton Bar
+ * @deprecated Use the W3C standard window.Notification API instead.
*/
NotificationMessenger.prototype.clear = function() {
- return PhoneGap.exec(null, null, 'StatusBarNotification', 'clear', []);
-};
+ if (this.activeNotification) {
+ this.activeNotification.close();
+ this.activeNotification = undefined;
+ }
+}
-/**
- * Load StatusBarNotification
- * */
+if (!window.plugins) window.plugins = {}
+if (!window.plugins.statusBarNotification) window.plugins.statusBarNotification = new NotificationMessenger();
-PhoneGap.addConstructor(function() {
- PhoneGap.addPlugin('statusBarNotification', new NotificationMessenger());
-
-// @deprecated: No longer needed in PhoneGap 1.0. Uncomment the addService code for earlier
-// PhoneGap releases.
-// PluginManager.addService("StatusBarNotificationPlugin","com.trial.phonegap.plugin.directorylisting.StatusBarNotificationPlugin");
-});
+
+/*
+ * The W3C standard API, window.Notification. See http://www.w3.org/TR/notifications/
+ * This API should be used for new applications instead of the old plugin API above.
+ */
+if (typeof window.Notification == 'undefined') {
+
+ /**
+ * Creates and shows a new notification.
+ * @param title
+ * @param options
+ */
+ window.Notification = function(title, options) {
+ options = options || {};
+ this.tag = options.tag || 'defaultTag';
+
+ // Add this notification to the global index by tag.
+ window.Notification.active[this.tag] = this;
+
+ // May be undefined.
+ this.onclick = options.onclick;
+ this.onerror = options.onerror;
+ this.onshow = options.onshow;
+ this.onclose = options.onclose;
+
+ var content = options.body || '';
+
+ cordova.exec(function() {
+ if (this.onshow) {
+ this.onshow();
+ }
+ }, function(error) {
+ if (this.onerror) {
+ this.onerror(error);
+ }
+ }, 'StatusBarNotification', 'notify', [this.tag, title, content]);
+ };
+
+ // Permission is always granted on Android.
+ window.Notification.permission = 'granted';
+
+ window.Notification.requestPermission = function(callback) {
+ callback('granted');
+ };
+
+ // Not part of the W3C API. Used by the native side to call onclick handlers.
+ window.Notification.callOnclickByTag = function(tag) {
+ console.log('callOnclickByTag');
+ var notification = window.Notification.active[tag];
+ if (notification && notification.onclick && typeof notification.onclick == 'function') {
+ console.log('inside if');
+ notification.onclick();
+ }
+ };
+
+ // A global map of notifications by tag, so their onclick callbacks can be called.
+ window.Notification.active = {};
+
+
+ /**
+ * Cancels a notification that has already been created and shown to the user.
+ */
+ window.Notification.prototype.close = function() {
+ cordova.exec(function() {
+ if (this.onclose) {
+ this.onclose();
+ }
+ }, function(error) {
+ if (this.onerror) {
+ this.onerror(error);
+ }
+ }, 'StatusBarNotification', 'clear', [this.tag]);
+ };
+}
+
+// vim: tabstop=4:softtabstop=4:shiftwidth=4:expandtab
diff --git a/Android/TTS/1.8.1/LICENSE b/Android/TTS/1.8.1/LICENSE
new file mode 100644
index 0000000..3da82a1
--- /dev/null
+++ b/Android/TTS/1.8.1/LICENSE
@@ -0,0 +1,64 @@
+PhoneGap is available under *either* the terms of the modified BSD license *or* the
+MIT License (2008). As a recipient of PhonegGap, you may choose which
+license to receive this code under (except as noted in per-module LICENSE
+files). Some modules may not be the copyright of Nitobi. These
+modules contain explicit declarations of copyright in both the LICENSE files in
+the directories in which they reside and in the code itself. No external
+contributions are allowed under licenses which are fundamentally incompatible
+with the MIT or BSD licenses that PhoneGap is distributed under.
+
+The text of the MIT and BSD licenses is reproduced below.
+
+-------------------------------------------------------------------------------
+The "New" BSD License:
+**********************
+
+Copyright (c) 2005-2010, Nitobi Software Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of Phonegap/Nitobi nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------------------------
+The MIT License
+*****************
+
+Copyright (c) <2010>
+
+ 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.
\ No newline at end of file
diff --git a/Android/TTS/config.xml b/Android/TTS/1.8.1/config.xml
similarity index 100%
rename from Android/TTS/config.xml
rename to Android/TTS/1.8.1/config.xml
diff --git a/Android/TTS/docs/TTS.md b/Android/TTS/1.8.1/docs/TTS.md
similarity index 100%
rename from Android/TTS/docs/TTS.md
rename to Android/TTS/1.8.1/docs/TTS.md
diff --git a/Android/TTS/manifest b/Android/TTS/1.8.1/manifest
similarity index 100%
rename from Android/TTS/manifest
rename to Android/TTS/1.8.1/manifest
diff --git a/Android/TTS/src/com/phonegap/plugins/speech/TTS.java b/Android/TTS/1.8.1/src/com/phonegap/plugins/speech/TTS.java
similarity index 100%
rename from Android/TTS/src/com/phonegap/plugins/speech/TTS.java
rename to Android/TTS/1.8.1/src/com/phonegap/plugins/speech/TTS.java
diff --git a/Android/TTS/www/tts.js b/Android/TTS/1.8.1/www/tts.js
similarity index 100%
rename from Android/TTS/www/tts.js
rename to Android/TTS/1.8.1/www/tts.js
diff --git a/Android/TTS/2.0.0/LICENSE b/Android/TTS/2.0.0/LICENSE
new file mode 100644
index 0000000..3da82a1
--- /dev/null
+++ b/Android/TTS/2.0.0/LICENSE
@@ -0,0 +1,64 @@
+PhoneGap is available under *either* the terms of the modified BSD license *or* the
+MIT License (2008). As a recipient of PhonegGap, you may choose which
+license to receive this code under (except as noted in per-module LICENSE
+files). Some modules may not be the copyright of Nitobi. These
+modules contain explicit declarations of copyright in both the LICENSE files in
+the directories in which they reside and in the code itself. No external
+contributions are allowed under licenses which are fundamentally incompatible
+with the MIT or BSD licenses that PhoneGap is distributed under.
+
+The text of the MIT and BSD licenses is reproduced below.
+
+-------------------------------------------------------------------------------
+The "New" BSD License:
+**********************
+
+Copyright (c) 2005-2010, Nitobi Software Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of Phonegap/Nitobi nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------------------------
+The MIT License
+*****************
+
+Copyright (c) <2010>
+
+ 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.
\ No newline at end of file
diff --git a/Android/TTS/2.0.0/config.xml b/Android/TTS/2.0.0/config.xml
new file mode 100644
index 0000000..55b78c8
--- /dev/null
+++ b/Android/TTS/2.0.0/config.xml
@@ -0,0 +1,33 @@
+
+
+
+
+ The TTS class that allows you to access the devices TTS services.
+
+
+
+The TTS class that allows you to access the devices TTS services.
+
+A simple use case would be:
+
+- Playing text passed into the service out as synthesized speech
+
+
+ Simon MacDonald
+
+
+PhoneGap is available under *either* the terms of the modified BSD license *or* the
+MIT License (2008). As a recipient of PhonegGap, you may choose which
+license to receive this code under (except as noted in per-module LICENSE
+files). Some modules may not be the copyright of Nitobi. These
+modules contain explicit declarations of copyright in both the LICENSE files in
+the directories in which they reside and in the code itself. No external
+contributions are allowed under licenses which are fundamentally incompatible
+with the MIT or BSD licenses that PhoneGap is distributed under.
+
+
\ No newline at end of file
diff --git a/Android/TTS/2.0.0/docs/TTS.md b/Android/TTS/2.0.0/docs/TTS.md
new file mode 100755
index 0000000..0729fa8
--- /dev/null
+++ b/Android/TTS/2.0.0/docs/TTS.md
@@ -0,0 +1,127 @@
+TTS
+==========
+
+The TTS class that allows you to access the devices TTS services.
+
+Properties
+----------
+
+N/A
+
+Methods
+-------
+
+- __startup__: starts the TTS service.
+- __shutdown__: stops the TTS service.
+- __speak__: speaks the specified text.
+- __silence__: plays silence for the specified number of ms.
+- __getLanguage__: gets the current TTS language.
+- __setLanguage__: sets the current TTS language.
+- __isLanguageAvailable__: finds out if TTS supports the language.
+
+
+Details
+-------
+
+The TTS class is a way to have your application read out text in a machine generated format.
+
+
+
+Supported Platforms
+-------------------
+
+- Android
+
+Quick Example
+------------------------------
+
+ window.plugins.tts.startup(startupWin, startupFail);
+
+ function startupWin(result) {
+ // When result is equal to STARTED we are ready to play
+ if (result == TTS.STARTED) {
+ window.plugins.tts.speak("The text to speech service is ready");
+ }
+ }
+
+ function startupFail(result) {
+ console.log("Startup failure = " + result);
+ }
+
+Full Example
+------------
+
+
+
+
+ PhoneGap Events Example
+
+
+
+
+
+
+
TTS Example
+
+
+ Speak
+ test
+
+
\ No newline at end of file
diff --git a/Android/TTS/2.0.0/manifest b/Android/TTS/2.0.0/manifest
new file mode 100644
index 0000000..945a250
--- /dev/null
+++ b/Android/TTS/2.0.0/manifest
@@ -0,0 +1,3 @@
+lib/commons-net-2.2.jar
+src/com/phonegap/plugins/FtpClient.java
+www/ftpclient.js
\ No newline at end of file
diff --git a/Android/TTS/2.0.0/src/com/phonegap/plugins/speech/TTS.java b/Android/TTS/2.0.0/src/com/phonegap/plugins/speech/TTS.java
new file mode 100644
index 0000000..c28101e
--- /dev/null
+++ b/Android/TTS/2.0.0/src/com/phonegap/plugins/speech/TTS.java
@@ -0,0 +1,210 @@
+/*
+ * PhoneGap is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ *
+ * Copyright (c) 2011, IBM Corporation
+ *
+ * Modified by Murray Macdonald (murray@workgroup.ca) on 2012/05/30 to add support for stop(), pitch(), speed() and interrupt();
+ *
+ */
+
+package com.phonegap.plugins.speech;
+
+import java.util.HashMap;
+import java.util.Locale;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.speech.tts.TextToSpeech;
+import android.speech.tts.TextToSpeech.OnInitListener;
+import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener;
+
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
+
+public class TTS extends Plugin implements OnInitListener, OnUtteranceCompletedListener {
+
+ private static final String LOG_TAG = "TTS";
+ private static final int STOPPED = 0;
+ private static final int INITIALIZING = 1;
+ private static final int STARTED = 2;
+ private TextToSpeech mTts = null;
+ private int state = STOPPED;
+
+ private String startupCallbackId = "";
+
+ @Override
+ public PluginResult execute(String action, JSONArray args, String callbackId) {
+ PluginResult.Status status = PluginResult.Status.OK;
+ String result = "";
+
+ try {
+ if (action.equals("speak")) {
+ String text = args.getString(0);
+ if (isReady()) {
+ HashMap map = null;
+ map = new HashMap();
+ map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, callbackId);
+ mTts.speak(text, TextToSpeech.QUEUE_ADD, map);
+ PluginResult pr = new PluginResult(PluginResult.Status.NO_RESULT);
+ pr.setKeepCallback(true);
+ return pr;
+ } else {
+ JSONObject error = new JSONObject();
+ error.put("message","TTS service is still initialzing.");
+ error.put("code", TTS.INITIALIZING);
+ return new PluginResult(PluginResult.Status.ERROR, error);
+ }
+ } else if (action.equals("interrupt")) {
+ String text = args.getString(0);
+ if (isReady()) {
+ HashMap map = null;
+ map = new HashMap();
+ map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, callbackId);
+ mTts.speak(text, TextToSpeech.QUEUE_FLUSH, map);
+ PluginResult pr = new PluginResult(PluginResult.Status.NO_RESULT);
+ pr.setKeepCallback(true);
+ return pr;
+ } else {
+ JSONObject error = new JSONObject();
+ error.put("message","TTS service is still initialzing.");
+ error.put("code", TTS.INITIALIZING);
+ return new PluginResult(PluginResult.Status.ERROR, error);
+ }
+ } else if (action.equals("stop")) {
+ if (isReady()) {
+ mTts.stop();
+ return new PluginResult(status, result);
+ } else {
+ JSONObject error = new JSONObject();
+ error.put("message","TTS service is still initialzing.");
+ error.put("code", TTS.INITIALIZING);
+ return new PluginResult(PluginResult.Status.ERROR, error);
+ }
+ } else if (action.equals("silence")) {
+ if (isReady()) {
+ mTts.playSilence(args.getLong(0), TextToSpeech.QUEUE_ADD, null);
+ return new PluginResult(status, result);
+ } else {
+ JSONObject error = new JSONObject();
+ error.put("message","TTS service is still initialzing.");
+ error.put("code", TTS.INITIALIZING);
+ return new PluginResult(PluginResult.Status.ERROR, error);
+ }
+ } else if (action.equals("speed")) {
+ if (isReady()) {
+ float speed= (float) (args.optLong(0, 100)) /(float) 100.0;
+ mTts.setSpeechRate(speed);
+ return new PluginResult(status, result);
+ } else {
+ JSONObject error = new JSONObject();
+ error.put("message","TTS service is still initialzing.");
+ error.put("code", TTS.INITIALIZING);
+ return new PluginResult(PluginResult.Status.ERROR, error);
+ }
+ } else if (action.equals("pitch")) {
+ if (isReady()) {
+ float pitch= (float) (args.optLong(0, 100)) /(float) 100.0;
+ mTts.setPitch(pitch);
+ return new PluginResult(status, result);
+ } else {
+ JSONObject error = new JSONObject();
+ error.put("message","TTS service is still initialzing.");
+ error.put("code", TTS.INITIALIZING);
+ return new PluginResult(PluginResult.Status.ERROR, error);
+ }
+ } else if (action.equals("startup")) {
+ if (mTts == null) {
+ this.startupCallbackId = callbackId;
+ state = TTS.INITIALIZING;
+ mTts = new TextToSpeech(cordova.getActivity().getApplicationContext(), this);
+ }
+ PluginResult pluginResult = new PluginResult(status, TTS.INITIALIZING);
+ pluginResult.setKeepCallback(true);
+ return pluginResult;
+ }
+ else if (action.equals("shutdown")) {
+ if (mTts != null) {
+ mTts.shutdown();
+ }
+ return new PluginResult(status, result);
+ }
+ else if (action.equals("getLanguage")) {
+ if (mTts != null) {
+ result = mTts.getLanguage().toString();
+ return new PluginResult(status, result);
+ }
+ }
+ else if (action.equals("isLanguageAvailable")) {
+ if (mTts != null) {
+ Locale loc = new Locale(args.getString(0));
+ int available = mTts.isLanguageAvailable(loc);
+ result = (available < 0) ? "false" : "true";
+ return new PluginResult(status, result);
+ }
+ }
+ else if (action.equals("setLanguage")) {
+ if (mTts != null) {
+ Locale loc = new Locale(args.getString(0));
+ int available = mTts.setLanguage(loc);
+ result = (available < 0) ? "false" : "true";
+ return new PluginResult(status, result);
+ }
+ }
+ return new PluginResult(status, result);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
+ }
+ }
+
+ /**
+ * Is the TTS service ready to play yet?
+ *
+ * @return
+ */
+ private boolean isReady() {
+ return (state == TTS.STARTED) ? true : false;
+ }
+
+ /**
+ * Called when the TTS service is initialized.
+ *
+ * @param status
+ */
+ public void onInit(int status) {
+ if (status == TextToSpeech.SUCCESS) {
+ state = TTS.STARTED;
+ PluginResult result = new PluginResult(PluginResult.Status.OK, TTS.STARTED);
+ result.setKeepCallback(false);
+ this.success(result, this.startupCallbackId);
+ mTts.setOnUtteranceCompletedListener(this);
+ }
+ else if (status == TextToSpeech.ERROR) {
+ state = TTS.STOPPED;
+ PluginResult result = new PluginResult(PluginResult.Status.ERROR, TTS.STOPPED);
+ result.setKeepCallback(false);
+ this.error(result, this.startupCallbackId);
+ }
+ }
+
+ /**
+ * Clean up the TTS resources
+ */
+ public void onDestroy() {
+ if (mTts != null) {
+ mTts.shutdown();
+ }
+ }
+
+ /**
+ * Once the utterance has completely been played call the speak's success callback
+ */
+ public void onUtteranceCompleted(String utteranceId) {
+ PluginResult result = new PluginResult(PluginResult.Status.OK);
+ result.setKeepCallback(false);
+ this.success(result, utteranceId);
+ }
+}
\ No newline at end of file
diff --git a/Android/TTS/2.0.0/www/tts.js b/Android/TTS/2.0.0/www/tts.js
new file mode 100644
index 0000000..b09b5c9
--- /dev/null
+++ b/Android/TTS/2.0.0/www/tts.js
@@ -0,0 +1,153 @@
+/*
+ * cordova is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ *
+ * Copyright (c) 2011, IBM Corporation
+ */
+/*
+ * cordova is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ *
+ * Copyright (c) 2011, IBM Corporation
+ *
+ * Modified by Murray Macdonald (murray@workgroup.ca) on 2012/05/30 to add pitch(), speed(), stop(), and interrupt() methods.
+ */
+
+/**
+ * Constructor
+ */
+function TTS() {
+}
+
+TTS.STOPPED = 0;
+TTS.INITIALIZING = 1;
+TTS.STARTED = 2;
+
+/**
+ * Play the passed in text as synthesized speech
+ *
+ * @param {DOMString} text
+ * @param {Object} successCallback
+ * @param {Object} errorCallback
+ */
+TTS.prototype.speak = function(text, successCallback, errorCallback) {
+ return cordova.exec(successCallback, errorCallback, "TTS", "speak", [text]);
+};
+
+/**
+ * Interrupt any existing speech, then speak the passed in text as synthesized speech
+ *
+ * @param {DOMString} text
+ * @param {Object} successCallback
+ * @param {Object} errorCallback
+ */
+TTS.prototype.interrupt = function(text, successCallback, errorCallback) {
+ return cordova.exec(successCallback, errorCallback, "TTS", "interrupt", [text]);
+};
+
+/**
+ * Stop any queued synthesized speech
+ *
+ * @param {DOMString} text
+ * @param {Object} successCallback
+ * @param {Object} errorCallback
+ */
+TTS.prototype.stop= function(successCallback, errorCallback) {
+ return cordova.exec(successCallback, errorCallback, "TTS", "stop", []);
+};
+
+/**
+ * Play silence for the number of ms passed in as duration
+ *
+ * @param {long} duration
+ * @param {Object} successCallback
+ * @param {Object} errorCallback
+ */
+TTS.prototype.silence = function(duration, successCallback, errorCallback) {
+ return cordova.exec(successCallback, errorCallback, "TTS", "silence", [duration]);
+};
+
+/**
+ * Set speed of speech. Usable from 30 to 500. Higher values make little difference.
+ *
+ * @param {long} speed
+ * @param {Object} successCallback
+ * @param {Object} errorCallback
+ */
+TTS.prototype.speed = function(speed, successCallback, errorCallback) {
+ return cordova.exec(successCallback, errorCallback, "TTS", "speed", [speed]);
+};
+
+/**
+ * Set pitch of speech. Useful values are approximately 30 - 300
+ *
+ * @param {long} pitch
+ * @param {Object} successCallback
+ * @param {Object} errorCallback
+ */
+TTS.prototype.pitch = function(pitch, successCallback, errorCallback) {
+ return cordova.exec(successCallback, errorCallback, "TTS", "pitch", [pitch]);
+};
+
+/**
+ * Starts up the TTS Service
+ *
+ * @param {Object} successCallback
+ * @param {Object} errorCallback
+ */
+TTS.prototype.startup = function(successCallback, errorCallback) {
+ return cordova.exec(successCallback, errorCallback, "TTS", "startup", []);
+};
+
+/**
+ * Shuts down the TTS Service if you no longer need it.
+ *
+ * @param {Object} successCallback
+ * @param {Object} errorCallback
+ */
+TTS.prototype.shutdown = function(successCallback, errorCallback) {
+ return cordova.exec(successCallback, errorCallback, "TTS", "shutdown", []);
+};
+
+/**
+ * Finds out if the language is currently supported by the TTS service.
+ *
+ * @param {DOMSting} lang
+ * @param {Object} successCallback
+ * @param {Object} errorCallback
+ */
+TTS.prototype.isLanguageAvailable = function(lang, successCallback, errorCallback) {
+ return cordova.exec(successCallback, errorCallback, "TTS", "isLanguageAvailable", [lang]);
+};
+
+/**
+ * Finds out the current language of the TTS service.
+ *
+ * @param {Object} successCallback
+ * @param {Object} errorCallback
+ */
+TTS.prototype.getLanguage = function(successCallback, errorCallback) {
+ return cordova.exec(successCallback, errorCallback, "TTS", "getLanguage", []);
+};
+
+/**
+ * Sets the language of the TTS service.
+ *
+ * @param {DOMString} lang
+ * @param {Object} successCallback
+ * @param {Object} errorCallback
+ */
+TTS.prototype.setLanguage = function(lang, successCallback, errorCallback) {
+ return cordova.exec(successCallback, errorCallback, "TTS", "setLanguage", [lang]);
+};
+
+/**
+ * Load TTS
+ */
+
+if(!window.plugins) {
+ window.plugins = {};
+}
+if (!window.plugins.tts) {
+ window.plugins.tts = new TTS();
+}
diff --git a/Android/Torch/README.md b/Android/Torch/1.9/README.md
similarity index 100%
rename from Android/Torch/README.md
rename to Android/Torch/1.9/README.md
diff --git a/Android/Torch/Torch.js b/Android/Torch/1.9/Torch.js
similarity index 100%
rename from Android/Torch/Torch.js
rename to Android/Torch/1.9/Torch.js
diff --git a/Android/Torch/TorchPlugin.java b/Android/Torch/1.9/TorchPlugin.java
similarity index 100%
rename from Android/Torch/TorchPlugin.java
rename to Android/Torch/1.9/TorchPlugin.java
diff --git a/Android/Torch/2.1/README.md b/Android/Torch/2.1/README.md
new file mode 100644
index 0000000..07e8565
--- /dev/null
+++ b/Android/Torch/2.1/README.md
@@ -0,0 +1,91 @@
+# Torch plugin for Phonegap (Android) #
+By Arne de Bree
+
+## Adding the Plugin to your project ##
+1. To install the plugin, move `Torch.js` to your project's www folder and include a reference to it
+in your html files.
+
+ <script src="Torch.js"></script>
+
+2. Create a folder called 'nl/debree/phonegap/plugin/torch' within your project's src folder.
+3. And copy the java file into that new folder.
+
+
+
+4. Add a plugin line to `res/xml/plugins.xml`
+
+ <plugin name="Torch" value="nl.debree.phonegap.plugin.torch.TorchPlugin" />
+
+## Using the plugin ##
+The plugin creates the object `window.plugins.Torch` within your DOM. This object
+exposes the following functions:
+
+- isOn
+- isCapable
+- toggle
+- turnOn
+- turnOff
+
+