Add inventory use, add code scanning, tweaks

master
Skylar Ittner 8 years ago
parent 5d2cc9e2f5
commit b6733a3f3d

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?> <?xml version="1.0" encoding="utf-8" standalone="no"?>
<widget xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0" xmlns:gap="http://phonegap.com/ns/1.0" id="com.netsyms.terranquest.TerranQuest" version="1.2"> <widget xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0" xmlns:gap="http://phonegap.com/ns/1.0" android-versionCode="103000" id="com.netsyms.terranquest.TerranQuest" version="1.3.0">
<name>TerranQuest</name> <name>TerranQuest</name>
<description> <description>
Augmented Reality fantasy game Augmented Reality fantasy game

@ -9,7 +9,7 @@
# For more information about plugins see http://cordova.apache.org/blog/releases/2013/07/23/cordova-3.html # For more information about plugins see http://cordova.apache.org/blog/releases/2013/07/23/cordova-3.html
# #
com.phonegap.plugins.barcodescanner=https://github.com/phonegap/phonegap-plugin-barcodescanner.git phonegap-plugin-barcodescanner=https://github.com/phonegap/phonegap-plugin-barcodescanner.git
cordova-plugin-whitelist=https://github.com/apache/cordova-plugin-whitelist.git cordova-plugin-whitelist=https://github.com/apache/cordova-plugin-whitelist.git
cordova-plugin-splashscreen=https://git-wip-us.apache.org/repos/asf/cordova-plugin-splashscreen.git cordova-plugin-splashscreen=https://git-wip-us.apache.org/repos/asf/cordova-plugin-splashscreen.git
cordova-plugin-dialogs=https://git-wip-us.apache.org/repos/asf/cordova-plugin-dialogs.git cordova-plugin-dialogs=https://git-wip-us.apache.org/repos/asf/cordova-plugin-dialogs.git

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<manifest android:hardwareAccelerated="true" android:versionCode="10200" android:versionName="1.2" package="com.netsyms.terranquest.TerranQuest" xmlns:android="http://schemas.android.com/apk/res/android"> <manifest android:hardwareAccelerated="true" android:versionCode="103000" android:versionName="1.3.0" package="com.netsyms.terranquest.TerranQuest" xmlns:android="http://schemas.android.com/apk/res/android">
<supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" /> <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<application android:hardwareAccelerated="true" android:icon="@drawable/icon" android:label="@string/app_name" android:supportsRtl="true"> <application android:hardwareAccelerated="true" android:icon="@drawable/icon" android:label="@string/app_name" android:supportsRtl="true">
@ -9,6 +9,24 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:clearTaskOnLaunch="true" android:configChanges="orientation|keyboardHidden|screenSize" android:exported="false" android:name="com.google.zxing.client.android.CaptureActivity" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:windowSoftInputMode="stateAlwaysHidden">
<intent-filter>
<action android:name="com.google.zxing.client.android.SCAN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:label="Share" android:name="com.google.zxing.client.android.encode.EncodeActivity">
<intent-filter>
<action android:name="com.phonegap.plugins.barcodescanner.ENCODE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:label="Share" android:name="com.google.zxing.client.android.HelpActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application> </application>
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="23" /> <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="23" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@ -19,4 +37,7 @@
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECORD_VIDEO" /> <uses-permission android:name="android.permission.RECORD_VIDEO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
</manifest> </manifest>

@ -24,12 +24,12 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:1.5.0' classpath 'com.android.tools.build:gradle:2.1.0'
} }
} }
apply plugin: 'android-library' apply plugin: 'com.android.library'
ext { ext {
apply from: 'cordova.gradle' apply from: 'cordova.gradle'

@ -1,2 +1,2 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<merger version="3"><dataSet config="main"><source path="/home/skylar/Documents/Projects/Sources/TerranQuest/platforms/android/CordovaLib/assets"/></dataSet><dataSet config="debug"><source path="/home/skylar/Documents/Projects/Sources/TerranQuest/platforms/android/CordovaLib/src/debug/assets"/></dataSet></merger> <merger version="3"><dataSet config="main"><source path="/home/skylar/Documents/Projects/Sources/TerranQuest/platforms/android/CordovaLib/assets"/><source path="/home/skylar/Documents/Projects/Sources/TerranQuest/platforms/android/CordovaLib/build/generated/assets/shaders/debug"/></dataSet><dataSet config="debug"><source path="/home/skylar/Documents/Projects/Sources/TerranQuest/platforms/android/CordovaLib/src/debug/assets"/></dataSet></merger>

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<merger version="3"><dataSet config="main"><source path="/home/skylar/Documents/Projects/Sources/TerranQuest/platforms/android/CordovaLib/src/main/shaders"/></dataSet><dataSet config="debug"><source path="/home/skylar/Documents/Projects/Sources/TerranQuest/platforms/android/CordovaLib/src/debug/shaders"/></dataSet></merger>

@ -1,2 +1,2 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<merger version="3"><dataSet config="main"><source path="/home/skylar/Documents/Projects/Sources/TerranQuest/platforms/android/CordovaLib/assets"/></dataSet><dataSet config="release"><source path="/home/skylar/Documents/Projects/Sources/TerranQuest/platforms/android/CordovaLib/src/release/assets"/></dataSet></merger> <merger version="3"><dataSet config="main"><source path="/home/skylar/Documents/Projects/Sources/TerranQuest/platforms/android/CordovaLib/assets"/><source path="/home/skylar/Documents/Projects/Sources/TerranQuest/platforms/android/CordovaLib/build/generated/assets/shaders/release"/></dataSet><dataSet config="release"><source path="/home/skylar/Documents/Projects/Sources/TerranQuest/platforms/android/CordovaLib/src/release/assets"/></dataSet></merger>

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<merger version="3"><dataSet config="main"><source path="/home/skylar/Documents/Projects/Sources/TerranQuest/platforms/android/CordovaLib/src/main/shaders"/></dataSet><dataSet config="release"><source path="/home/skylar/Documents/Projects/Sources/TerranQuest/platforms/android/CordovaLib/src/release/shaders"/></dataSet></merger>

@ -61,7 +61,7 @@ String doFindLatestInstalledBuildTools(String minBuildToolsVersion) {
highestBuildToolsVersion highestBuildToolsVersion
} else { } else {
throw new RuntimeException( throw new RuntimeException(
"No installed build tools found. Please install the Android build tools version " + "No installed build tools found. Install the Android build tools version " +
minBuildToolsVersion + " or higher.") minBuildToolsVersion + " or higher.")
} }
} }

@ -0,0 +1,65 @@
/*
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.
*/
package org.apache.cordova;
import android.util.Pair;
import android.util.SparseArray;
/**
* Provides a collection that maps unique request codes to CordovaPlugins and Integers.
* Used to ensure that when plugins make requests for runtime permissions, those requests do not
* collide with requests from other plugins that use the same request code value.
*/
public class CallbackMap {
private int currentCallbackId = 0;
private SparseArray<Pair<CordovaPlugin, Integer>> callbacks;
public CallbackMap() {
this.callbacks = new SparseArray<Pair<CordovaPlugin, Integer>>();
}
/**
* Stores a CordovaPlugin and request code and returns a new unique request code to use
* in a permission request.
*
* @param receiver The plugin that is making the request
* @param requestCode The original request code used by the plugin
* @return A unique request code that can be used to retrieve this callback
* with getAndRemoveCallback()
*/
public synchronized int registerCallback(CordovaPlugin receiver, int requestCode) {
int mappedId = this.currentCallbackId++;
callbacks.put(mappedId, new Pair<CordovaPlugin, Integer>(receiver, requestCode));
return mappedId;
}
/**
* Retrieves and removes a callback stored in the map using the mapped request code
* obtained from registerCallback()
*
* @param mappedId The request code obtained from registerCallback()
* @return The CordovaPlugin and orignal request code that correspond to the
* given mappedCode
*/
public synchronized Pair<CordovaPlugin, Integer> getAndRemoveCallback(int mappedId) {
Pair<CordovaPlugin, Integer> callback = callbacks.get(mappedId);
callbacks.remove(mappedId);
return callback;
}
}

@ -25,6 +25,7 @@ import android.content.pm.PackageManager;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.util.Pair;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
@ -42,8 +43,8 @@ public class CordovaInterfaceImpl implements CordovaInterface {
protected PluginManager pluginManager; protected PluginManager pluginManager;
protected ActivityResultHolder savedResult; protected ActivityResultHolder savedResult;
protected CallbackMap permissionResultCallbacks;
protected CordovaPlugin activityResultCallback; protected CordovaPlugin activityResultCallback;
protected CordovaPlugin permissionResultCallback;
protected String initCallbackService; protected String initCallbackService;
protected int activityResultRequestCode; protected int activityResultRequestCode;
protected boolean activityWasDestroyed = false; protected boolean activityWasDestroyed = false;
@ -56,6 +57,7 @@ public class CordovaInterfaceImpl implements CordovaInterface {
public CordovaInterfaceImpl(Activity activity, ExecutorService threadPool) { public CordovaInterfaceImpl(Activity activity, ExecutorService threadPool) {
this.activity = activity; this.activity = activity;
this.threadPool = threadPool; this.threadPool = threadPool;
this.permissionResultCallbacks = new CallbackMap();
} }
@Override @Override
@ -208,24 +210,21 @@ public class CordovaInterfaceImpl implements CordovaInterface {
*/ */
public void onRequestPermissionResult(int requestCode, String[] permissions, public void onRequestPermissionResult(int requestCode, String[] permissions,
int[] grantResults) throws JSONException { int[] grantResults) throws JSONException {
if(permissionResultCallback != null) Pair<CordovaPlugin, Integer> callback = permissionResultCallbacks.getAndRemoveCallback(requestCode);
{ if(callback != null) {
permissionResultCallback.onRequestPermissionResult(requestCode, permissions, grantResults); callback.first.onRequestPermissionResult(callback.second, permissions, grantResults);
permissionResultCallback = null;
} }
} }
public void requestPermission(CordovaPlugin plugin, int requestCode, String permission) { public void requestPermission(CordovaPlugin plugin, int requestCode, String permission) {
permissionResultCallback = plugin;
String[] permissions = new String [1]; String[] permissions = new String [1];
permissions[0] = permission; permissions[0] = permission;
getActivity().requestPermissions(permissions, requestCode); requestPermissions(plugin, requestCode, permissions);
} }
public void requestPermissions(CordovaPlugin plugin, int requestCode, String [] permissions) public void requestPermissions(CordovaPlugin plugin, int requestCode, String [] permissions) {
{ int mappedRequestCode = permissionResultCallbacks.registerCallback(plugin, requestCode);
permissionResultCallback = plugin; getActivity().requestPermissions(permissions, mappedRequestCode);
getActivity().requestPermissions(permissions, requestCode);
} }
public boolean hasPermission(String permission) public boolean hasPermission(String permission)

@ -31,7 +31,7 @@ import android.webkit.WebChromeClient.CustomViewCallback;
* are not expected to implement it. * are not expected to implement it.
*/ */
public interface CordovaWebView { public interface CordovaWebView {
public static final String CORDOVA_VERSION = "5.1.1"; public static final String CORDOVA_VERSION = "5.2.1";
void init(CordovaInterface cordova, List<PluginEntry> pluginEntries, CordovaPreferences preferences); void init(CordovaInterface cordova, List<PluginEntry> pluginEntries, CordovaPreferences preferences);

@ -135,6 +135,7 @@ public class CordovaWebViewImpl implements CordovaWebView {
if (recreatePlugins) { if (recreatePlugins) {
// Don't re-initialize on first load. // Don't re-initialize on first load.
if (loadedUrl != null) { if (loadedUrl != null) {
appPlugin = null;
pluginManager.init(); pluginManager.init();
} }
loadedUrl = url; loadedUrl = url;

@ -37,6 +37,9 @@ class SystemCookieManager implements ICordovaCookieManager {
webView = webview; webView = webview;
cookieManager = CookieManager.getInstance(); cookieManager = CookieManager.getInstance();
//REALLY? Nobody has seen this UNTIL NOW?
cookieManager.setAcceptFileSchemeCookies(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
cookieManager.setAcceptThirdPartyCookies(webView, true); cookieManager.setAcceptThirdPartyCookies(webView, true);
} }

@ -47,6 +47,10 @@
{ {
"xml": "<feature name=\"Whitelist\"><param name=\"android-package\" value=\"org.apache.cordova.whitelist.WhitelistPlugin\" /><param name=\"onload\" value=\"true\" /></feature>", "xml": "<feature name=\"Whitelist\"><param name=\"android-package\" value=\"org.apache.cordova.whitelist.WhitelistPlugin\" /><param name=\"onload\" value=\"true\" /></feature>",
"count": 1 "count": 1
},
{
"xml": "<feature name=\"BarcodeScanner\"><param name=\"android-package\" value=\"com.phonegap.plugins.barcodescanner.BarcodeScanner\" /></feature>",
"count": 1
} }
] ]
} }
@ -87,8 +91,34 @@
"count": 1 "count": 1
} }
], ],
"/manifest/application": [], "/manifest/application": [
"/manifest": [] {
"xml": "<activity android:clearTaskOnLaunch=\"true\" android:configChanges=\"orientation|keyboardHidden|screenSize\" android:exported=\"false\" android:name=\"com.google.zxing.client.android.CaptureActivity\" android:theme=\"@android:style/Theme.NoTitleBar.Fullscreen\" android:windowSoftInputMode=\"stateAlwaysHidden\"><intent-filter><action android:name=\"com.google.zxing.client.android.SCAN\" /><category android:name=\"android.intent.category.DEFAULT\" /></intent-filter></activity>",
"count": 1
},
{
"xml": "<activity android:label=\"Share\" android:name=\"com.google.zxing.client.android.encode.EncodeActivity\"><intent-filter><action android:name=\"com.phonegap.plugins.barcodescanner.ENCODE\" /><category android:name=\"android.intent.category.DEFAULT\" /></intent-filter></activity>",
"count": 1
},
{
"xml": "<activity android:label=\"Share\" android:name=\"com.google.zxing.client.android.HelpActivity\"><intent-filter><action android:name=\"android.intent.action.VIEW\" /><category android:name=\"android.intent.category.DEFAULT\" /></intent-filter></activity>",
"count": 1
}
],
"/manifest": [
{
"xml": "<uses-permission android:name=\"android.permission.CAMERA\" />",
"count": 1
},
{
"xml": "<uses-permission android:name=\"android.permission.FLASHLIGHT\" />",
"count": 1
},
{
"xml": "<uses-feature android:name=\"android.hardware.camera\" android:required=\"false\" />",
"count": 1
}
]
} }
} }
} }
@ -129,6 +159,9 @@
}, },
"cordova-plugin-whitelist": { "cordova-plugin-whitelist": {
"PACKAGE_NAME": "com.netsyms.terranquest.TerranQuest" "PACKAGE_NAME": "com.netsyms.terranquest.TerranQuest"
},
"phonegap-plugin-barcodescanner": {
"PACKAGE_NAME": "com.netsyms.terranquest.TerranQuest"
} }
}, },
"dependent_plugins": {}, "dependent_plugins": {},
@ -434,6 +467,14 @@
"file": "plugins/cordova-plugin-whitelist/whitelist.js", "file": "plugins/cordova-plugin-whitelist/whitelist.js",
"id": "cordova-plugin-whitelist.whitelist", "id": "cordova-plugin-whitelist.whitelist",
"runs": true "runs": true
},
{
"id": "phonegap-plugin-barcodescanner.BarcodeScanner",
"file": "plugins/phonegap-plugin-barcodescanner/www/barcodescanner.js",
"pluginId": "phonegap-plugin-barcodescanner",
"clobbers": [
"cordova.plugins.barcodeScanner"
]
} }
], ],
"plugin_metadata": { "plugin_metadata": {
@ -448,6 +489,7 @@
"cordova-plugin-media-capture": "1.2.1-dev", "cordova-plugin-media-capture": "1.2.1-dev",
"cordova-plugin-network-information": "1.2.1-dev", "cordova-plugin-network-information": "1.2.1-dev",
"cordova-plugin-splashscreen": "3.2.2-dev", "cordova-plugin-splashscreen": "3.2.2-dev",
"cordova-plugin-whitelist": "1.2.1" "cordova-plugin-whitelist": "1.2.1",
"phonegap-plugin-barcodescanner": "6.0.1"
} }
} }

@ -0,0 +1 @@
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M576 1152v128h-128v-128h128zm0-768v128h-128v-128h128zm768 0v128h-128v-128h128zm-1024 1023h384v-383h-384v383zm0-767h384v-384h-384v384zm768 0h384v-384h-384v384zm-256 256v640h-640v-640h640zm512 512v128h-128v-128h128zm256 0v128h-128v-128h128zm0-512v384h-384v-128h-128v384h-128v-640h384v128h128v-128h128zm-768-768v640h-640v-640h640zm768 0v640h-640v-640h640z"/></svg>

After

Width:  |  Height:  |  Size: 461 B

@ -1,5 +1,5 @@
// Platform: android // Platform: android
// c517ca811b4948b630e0b74dbae6c9637939da24 // 2fd4bcb84048415922d13d80d35b8d1668e8e150
/* /*
Licensed to the Apache Software Foundation (ASF) under one Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file or more contributor license agreements. See the NOTICE file
@ -19,7 +19,7 @@
under the License. under the License.
*/ */
;(function() { ;(function() {
var PLATFORM_VERSION_BUILD_LABEL = '5.1.1'; var PLATFORM_VERSION_BUILD_LABEL = '5.2.1';
// file: src/scripts/require.js // file: src/scripts/require.js
/*jshint -W079 */ /*jshint -W079 */
@ -330,7 +330,7 @@ module.exports = cordova;
}); });
// file: /Users/steveng/repo/cordova/cordova-android/cordova-js-src/android/nativeapiprovider.js // file: D:/cordova/cordova-android/cordova-js-src/android/nativeapiprovider.js
define("cordova/android/nativeapiprovider", function(require, exports, module) { define("cordova/android/nativeapiprovider", function(require, exports, module) {
/** /**
@ -353,7 +353,7 @@ module.exports = {
}); });
// file: /Users/steveng/repo/cordova/cordova-android/cordova-js-src/android/promptbasednativeapi.js // file: D:/cordova/cordova-android/cordova-js-src/android/promptbasednativeapi.js
define("cordova/android/promptbasednativeapi", function(require, exports, module) { define("cordova/android/promptbasednativeapi", function(require, exports, module) {
/** /**
@ -862,7 +862,7 @@ module.exports = channel;
}); });
// file: /Users/steveng/repo/cordova/cordova-android/cordova-js-src/exec.js // file: D:/cordova/cordova-android/cordova-js-src/exec.js
define("cordova/exec", function(require, exports, module) { define("cordova/exec", function(require, exports, module) {
/** /**
@ -1611,7 +1611,7 @@ exports.reset();
}); });
// file: /Users/steveng/repo/cordova/cordova-android/cordova-js-src/platform.js // file: D:/cordova/cordova-android/cordova-js-src/platform.js
define("cordova/platform", function(require, exports, module) { define("cordova/platform", function(require, exports, module) {
// The last resume event that was received that had the result of a plugin call. // The last resume event that was received that had the result of a plugin call.
@ -1721,7 +1721,7 @@ function onMessageFromNative(msg) {
}); });
// file: /Users/steveng/repo/cordova/cordova-android/cordova-js-src/plugin/android/app.js // file: D:/cordova/cordova-android/cordova-js-src/plugin/android/app.js
define("cordova/plugin/android/app", function(require, exports, module) { define("cordova/plugin/android/app", function(require, exports, module) {
var exec = require('cordova/exec'); var exec = require('cordova/exec');

@ -301,6 +301,14 @@ module.exports = [
"file": "plugins/cordova-plugin-whitelist/whitelist.js", "file": "plugins/cordova-plugin-whitelist/whitelist.js",
"id": "cordova-plugin-whitelist.whitelist", "id": "cordova-plugin-whitelist.whitelist",
"runs": true "runs": true
},
{
"id": "phonegap-plugin-barcodescanner.BarcodeScanner",
"file": "plugins/phonegap-plugin-barcodescanner/www/barcodescanner.js",
"pluginId": "phonegap-plugin-barcodescanner",
"clobbers": [
"cordova.plugins.barcodeScanner"
]
} }
]; ];
module.exports.metadata = module.exports.metadata =
@ -317,7 +325,8 @@ module.exports.metadata =
"cordova-plugin-media-capture": "1.2.1-dev", "cordova-plugin-media-capture": "1.2.1-dev",
"cordova-plugin-network-information": "1.2.1-dev", "cordova-plugin-network-information": "1.2.1-dev",
"cordova-plugin-splashscreen": "3.2.2-dev", "cordova-plugin-splashscreen": "3.2.2-dev",
"cordova-plugin-whitelist": "1.2.1" "cordova-plugin-whitelist": "1.2.1",
"phonegap-plugin-barcodescanner": "6.0.1"
}; };
// BOTTOM OF METADATA // BOTTOM OF METADATA
}); });

@ -226,12 +226,13 @@ body {
box-shadow: 0 0 5px grey; box-shadow: 0 0 5px grey;
width: 60px; width: 60px;
height: 60px; height: 60px;
margin-bottom: 20px;
} }
#buttons .circlebutton img { #buttons .circlebutton img {
position: absolute; position: static;
top: 15px; margin-top: 14px;
left: 15px; margin-left: 14px;
width: 30px; width: 30px;
height: 30px; height: 30px;
} }

@ -18,28 +18,36 @@ function askLogout() {
} }
function logout() { function logout() {
localStorage.setItem("username", ''); $.getJSON(mkApiUrl('deletesession', 'gs'), {}, function (data) {
localStorage.setItem("password", ''); if (data.status === 'OK') {
username = null; localStorage.setItem("username", '');
password = null; localStorage.setItem("password", '');
$('#content-zone').load("screens/login.html"); username = null;
password = null;
$('#content-zone').load("screens/login.html");
} else {
navigator.notification.alert("Server did not properly acknowledge logout. You might have problems for the next few hours if you switch accounts.", null, "Error", 'Dismiss');
}
}).fail(function () {
navigator.notification.alert("Cannot connect to authentication server. Check your Internet connection and try again. If that fails, clear the app data or reinstall TerranQuest.", null, "Error", 'Dismiss');
});
} }
function checkUserHasTeamOpenChooserIfNot(username) { function checkUserHasTeamOpenChooserIfNot(username) {
$.getJSON(mkApiUrl('getstats', 'gs'), { $.getJSON(mkApiUrl('getstats', 'gs'), {
user: username user: username
}, function (data) { }, function (data) {
if (data.status === 'OK' && data.stats.teamid !== null && data.stats.teamid > 0) { if (data.status === 'OK' && data.stats.teamid !== null && data.stats.teamid > 0) {
// We're all good. // We're all good.
userteamid = data.stats.teamid; userteamid = data.stats.teamid;
openscreen("home"); openscreen("home");
} else { } else {
// Open the team intro thingy // Open the team intro thingy
openscreen('chooseteam'); openscreen('chooseteam');
} }
}).fail(function () { }).fail(function () {
}); });
} }
function dosignup() { function dosignup() {
@ -71,21 +79,21 @@ function dosignup() {
name: $('#nameBox').val(), name: $('#nameBox').val(),
email: $('#emailBox').val() email: $('#emailBox').val()
}, },
function (data) { function (data) {
if (data === 'OK') { if (data === 'OK') {
username = $('#usernameBox').val().toLowerCase(); username = $('#usernameBox').val().toLowerCase();
password = $('#passwordBox').val(); password = $('#passwordBox').val();
localStorage.setItem("username", username); localStorage.setItem("username", username);
localStorage.setItem("password", password); localStorage.setItem("password", password);
checkUserHasTeamOpenChooserIfNot(username); checkUserHasTeamOpenChooserIfNot(username);
} else { } else {
$('#signupBtn').html('<i class="fa fa-user-plus"></i> Sign Up'); $('#signupBtn').html('<i class="fa fa-user-plus"></i> Sign Up');
$('#signupBtn').attr('disabled', false); $('#signupBtn').attr('disabled', false);
$('#errormsg').text("Error: " + data); $('#errormsg').text("Error: " + data);
$('#errorbase').css('display', 'block'); $('#errorbase').css('display', 'block');
} }
authOpInProgress = false; authOpInProgress = false;
}).fail(function () { }).fail(function () {
$('#signupBtn').html('<i class="fa fa-user-plus"></i> Sign Up'); $('#signupBtn').html('<i class="fa fa-user-plus"></i> Sign Up');
$('#signupBtn').attr('disabled', false); $('#signupBtn').attr('disabled', false);
$('#errormsg').text("Error: Network failure."); $('#errormsg').text("Error: Network failure.");
@ -111,42 +119,42 @@ function dologin() {
$('#loginBtn').html('<i class="fa fa-cog fa-spin fa-fw"></i> Logging in...'); $('#loginBtn').html('<i class="fa fa-cog fa-spin fa-fw"></i> Logging in...');
$.post("https://sso.netsyms.com/api/simpleauth.php", $.post("https://sso.netsyms.com/api/simpleauth.php",
{user: $('#usernameBox').val(), pass: $('#passwordBox').val()}, {user: $('#usernameBox').val(), pass: $('#passwordBox').val()},
function (data) { function (data) {
if (data === 'OK') { if (data === 'OK') {
// Now that auth is OK, ping the game server // Now that auth is OK, ping the game server
$.getJSON(mkApiUrl('pinglogin') + "?user=" + $('#usernameBox').val(), function (out) { $.getJSON(mkApiUrl('pinglogin') + "?user=" + $('#usernameBox').val(), function (out) {
if (out.status === 'OK') { if (out.status === 'OK') {
username = $('#usernameBox').val().toLowerCase(); username = $('#usernameBox').val().toLowerCase();
password = $('#passwordBox').val(); password = $('#passwordBox').val();
localStorage.setItem("username", username); localStorage.setItem("username", username);
localStorage.setItem("password", password); localStorage.setItem("password", password);
navigator.splashscreen.hide(); navigator.splashscreen.hide();
checkUserHasTeamOpenChooserIfNot(username); checkUserHasTeamOpenChooserIfNot(username);
} else {
$('#loginBtn').html('<i class="fa fa-sign-in"></i> Login');
$('#loginBtn').attr('disabled', false);
$('#errormsg').text("Error: " + out.message);
$('#errorbase').css('display', 'block');
$('#loading').css('display', 'none');
authOpInProgress = false;
}
}).fail(function (err) {
$('#loginBtn').html('<i class="fa fa-sign-in"></i> Login');
$('#loginBtn').attr('disabled', false);
$('#errormsg').text("Error: Login OK, but cannot connect to game server. Try again later.");
$('#errorbase').css('display', 'block');
$('#loading').css('display', 'none');
authOpInProgress = false;
});
} else { } else {
$('#loginBtn').html('<i class="fa fa-sign-in"></i> Login'); $('#loginBtn').html('<i class="fa fa-sign-in"></i> Login');
$('#loginBtn').attr('disabled', false); $('#loginBtn').attr('disabled', false);
$('#errormsg').text("Error: " + out.message); $('#errormsg').text(data);
$('#errorbase').css('display', 'block'); $('#errorbase').css('display', 'block');
$('#loading').css('display', 'none'); $('#loading').css('display', 'none');
authOpInProgress = false;
} }
}).fail(function (err) {
$('#loginBtn').html('<i class="fa fa-sign-in"></i> Login');
$('#loginBtn').attr('disabled', false);
$('#errormsg').text("Error: Login OK, but cannot connect to game server. Try again later.");
$('#errorbase').css('display', 'block');
$('#loading').css('display', 'none');
authOpInProgress = false; authOpInProgress = false;
}); }).fail(function () {
} else {
$('#loginBtn').html('<i class="fa fa-sign-in"></i> Login');
$('#loginBtn').attr('disabled', false);
$('#errormsg').text(data);
$('#errorbase').css('display', 'block');
$('#loading').css('display', 'none');
}
authOpInProgress = false;
}).fail(function () {
$('#loginBtn').html('<i class="fa fa-sign-in"></i> Login'); $('#loginBtn').html('<i class="fa fa-sign-in"></i> Login');
$('#loginBtn').attr('disabled', false); $('#loginBtn').attr('disabled', false);
$('#errormsg').text("Error: Network failure."); $('#errormsg').text("Error: Network failure.");

@ -83,7 +83,18 @@ function scanCode() {
cordova.plugins.barcodeScanner.scan( cordova.plugins.barcodeScanner.scan(
function (result) { function (result) {
if (!result.cancelled) { if (!result.cancelled) {
navigator.notification.alert("Scanned code: " + result.text, null, "OK", 'Dismiss'); $.getJSON(mkApiUrl('code2item', 'gs'), {
code: result.text
}, function (data) {
if (data.status === 'OK') {
navigator.notification.alert("Found one " + data.message, null, "Found an item!", 'OK');
} else {
navigator.notification.alert(data.message, null, "Huh?", 'OK');
}
}).fail(function () {
navigator.notification.alert("Nothing happened!", null, "Huh?", 'OK');
});
//navigator.notification.alert("Scanned code: " + result.text, null, "OK", 'Dismiss');
} }
}, },
function (error) { function (error) {
@ -91,7 +102,7 @@ function scanCode() {
} }
); );
} catch (ex) { } catch (ex) {
alert(ex.message); navigator.notification.alert(ex.message, null, "Error", 'Dismiss');
} }
} }

@ -11,7 +11,7 @@
* Syncs the user's stats with the server and calls refreshStats(). * Syncs the user's stats with the server and calls refreshStats().
*/ */
function syncStats() { function syncStats() {
$.getJSON(mkApiUrl('getstats'), { $.getJSON(mkApiUrl('getstats', 'gs'), {
user: username user: username
}, function (data) { }, function (data) {
if (data.status === 'OK') { if (data.status === 'OK') {

@ -0,0 +1,150 @@
cordova.define("phonegap-plugin-barcodescanner.BarcodeScanner", function(require, exports, module) {
/**
* 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) Matt Kane 2010
* Copyright (c) 2011, IBM Corporation
*/
var exec = require("cordova/exec");
var scanInProgress = false;
/**
* Constructor.
*
* @returns {BarcodeScanner}
*/
function BarcodeScanner() {
/**
* Encoding constants.
*
* @type Object
*/
this.Encode = {
TEXT_TYPE: "TEXT_TYPE",
EMAIL_TYPE: "EMAIL_TYPE",
PHONE_TYPE: "PHONE_TYPE",
SMS_TYPE: "SMS_TYPE"
// CONTACT_TYPE: "CONTACT_TYPE", // TODO: not implemented, requires passing a Bundle class from Javascript to Java
// LOCATION_TYPE: "LOCATION_TYPE" // TODO: not implemented, requires passing a Bundle class from Javascript to Java
};
/**
* Barcode format constants, defined in ZXing library.
*
* @type Object
*/
this.format = {
"all_1D": 61918,
"aztec": 1,
"codabar": 2,
"code_128": 16,
"code_39": 4,
"code_93": 8,
"data_MATRIX": 32,
"ean_13": 128,
"ean_8": 64,
"itf": 256,
"maxicode": 512,
"msi": 131072,
"pdf_417": 1024,
"plessey": 262144,
"qr_CODE": 2048,
"rss_14": 4096,
"rss_EXPANDED": 8192,
"upc_A": 16384,
"upc_E": 32768,
"upc_EAN_EXTENSION": 65536
};
}
/**
* Read code from scanner.
*
* @param {Function} successCallback This function will recieve a result object: {
* text : '12345-mock', // The code that was scanned.
* format : 'FORMAT_NAME', // Code format.
* cancelled : true/false, // Was canceled.
* }
* @param {Function} errorCallback
* @param config
*/
BarcodeScanner.prototype.scan = function (successCallback, errorCallback, config) {
if (config instanceof Array) {
// do nothing
} else {
if (typeof(config) === 'object') {
config = [ config ];
} else {
config = [];
}
}
if (errorCallback == null) {
errorCallback = function () {
};
}
if (typeof errorCallback != "function") {
console.log("BarcodeScanner.scan failure: failure parameter not a function");
return;
}
if (typeof successCallback != "function") {
console.log("BarcodeScanner.scan failure: success callback parameter must be a function");
return;
}
if (scanInProgress) {
errorCallback('Scan is already in progress');
return;
}
scanInProgress = true;
exec(
function(result) {
scanInProgress = false;
successCallback(result);
},
function(error) {
scanInProgress = false;
errorCallback(error);
},
'BarcodeScanner',
'scan',
config
);
};
//-------------------------------------------------------------------
BarcodeScanner.prototype.encode = function (type, data, successCallback, errorCallback, options) {
if (errorCallback == null) {
errorCallback = function () {
};
}
if (typeof errorCallback != "function") {
console.log("BarcodeScanner.encode failure: failure parameter not a function");
return;
}
if (typeof successCallback != "function") {
console.log("BarcodeScanner.encode failure: success callback parameter must be a function");
return;
}
exec(successCallback, errorCallback, 'BarcodeScanner', 'encode', [
{"type": type, "data": data, "options": options}
]);
};
var barcodeScanner = new BarcodeScanner();
module.exports = barcodeScanner;
});

@ -32,7 +32,7 @@
function setTeam() { function setTeam() {
//alert($('input[name=teamChooser]:checked').val()); //alert($('input[name=teamChooser]:checked').val());
var team = $('input[name=teamChooser]:checked').val(); var team = $('input[name=teamChooser]:checked').val();
$.getJSON(mkApiUrl('setteam', 'gs'), { $.getJSON(mkApiUrl('setteam', 'gs'), {
teamid: team teamid: team
}, function (data) { }, function (data) {
if (data.status === 'OK') { if (data.status === 'OK') {
@ -40,7 +40,7 @@
openscreen("home"); openscreen("home");
} else { } else {
// Error? // Error?
alert("Error: " + data.message); navigator.notification.alert(data.message, null, "Error", 'OK');
} }
}).fail(function () { }).fail(function () {
alert("Something bad happened, try again later."); alert("Something bad happened, try again later.");

@ -36,6 +36,9 @@
<div class="circlebutton" onclick="openMenu()"> <div class="circlebutton" onclick="openMenu()">
<img src="assets/bars.svg" alt="Menu" /> <img src="assets/bars.svg" alt="Menu" />
</div> </div>
<div class="circlebutton" onclick="scanCode()">
<img src="assets/qrcode.svg" alt="Scan Code" />
</div>
</div> </div>
<div id="chatbox"> <div id="chatbox">

@ -7,15 +7,36 @@
</div> </div>
<script> <script>
function useitem(uuid) {
$.getJSON(mkApiUrl('useitem', 'gs'), {
itemuuid: uuid
}, function (data) {
if (data.status === 'OK') {
loadinventory();
syncStats();
} else {
navigator.notification.alert(data.message, null, "Error", 'OK');
}
}).fail(function () {
navigator.notification.alert("Cannot use item. Try again.", null, "Error", 'OK');
});
}
function getitemhtmlfromjson(item) { function getitemhtmlfromjson(item) {
return "\ var itemhtml = "\
<div class='list-group-item inventory-item' id='item-" + item.itemuuid + "'>\ <div class='list-group-item inventory-item' id='item-" + item.itemuuid + "'>\
<h4 class='itemname'>" + item.itemname + "</h4>\ <h4 class='itemname'>" + item.itemname + "</h4>\
<p class='itemdesc'>" + item.itemdesc + "</p>\ <p class='itemdesc'>" + item.itemdesc + "</p>";
<span class='itemid' style='display: none;'>" + item.itemid + "</span>\ if (item.classname == "healmagic") {
itemhtml += "<span class='btn btn-success' onclick=\"useitem('" + item.itemuuid + "')\">\
Use Item\
</span>";
}
itemhtml += "<span class='itemid' style='display: none;'>" + item.itemid + "</span>\
<span class='itemclassid' style='display: none;'>" + item.classid + "</span>\ <span class='itemclassid' style='display: none;'>" + item.classid + "</span>\
<span class='itemjson' style='display: none;'>" + item.itemjson + "</span>\ <span class='itemjson' style='display: none;'>" + item.itemjson + "</span>\
</div>"; </div>";
return itemhtml;
} }
function loadinventory() { function loadinventory() {
@ -34,6 +55,6 @@
$('#inventory-list').html(content); $('#inventory-list').html(content);
}); });
} }
loadinventory(); loadinventory();
</script> </script>

@ -10,9 +10,11 @@
<script> <script>
var thisplace = null; var thisplace = null;
var placeteam = 0; var placeteam = 0;
var capturebtndisabled = false;
function resetcapturebtn() { function resetcapturebtn() {
capturebtndisabled = false;
$('#capturebtn').removeClass('btn-warning'); $('#capturebtn').removeClass('btn-warning');
$('#capturebtn').addClass('btn-primary'); $('#capturebtn').addClass('btn-primary');
$('#capturebtn').removeClass('disabled'); $('#capturebtn').removeClass('disabled');
@ -64,6 +66,7 @@
} }
function attemptcapture() { function attemptcapture() {
capturebtndisabled = true;
$('#capturebtn').prop('disabled', true); $('#capturebtn').prop('disabled', true);
$('#capturebtn').addClass('disabled'); $('#capturebtn').addClass('disabled');
$.getJSON(mkApiUrl('attackplace', 'gs'), { $.getJSON(mkApiUrl('attackplace', 'gs'), {
@ -88,6 +91,7 @@
} }
function attemptclaim() { function attemptclaim() {
capturebtndisabled = true;
$('#capturebtn').prop('disabled', true); $('#capturebtn').prop('disabled', true);
$('#capturebtn').addClass('disabled'); $('#capturebtn').addClass('disabled');
$.getJSON(mkApiUrl('claimplace', 'gs'), { $.getJSON(mkApiUrl('claimplace', 'gs'), {
@ -111,6 +115,9 @@
} }
function attempttake() { function attempttake() {
if (capturebtndisabled) {
return;
}
if (placeteam == 0) { if (placeteam == 0) {
attemptclaim(); attemptclaim();
} else if (placeteam == userteamid) { } else if (placeteam == userteamid) {

@ -17,13 +17,12 @@
under the License. under the License.
*/ */
// GENERATED FILE! DO NOT EDIT! apply plugin: 'com.android.application'
apply plugin: 'android'
buildscript { buildscript {
repositories { repositories {
mavenCentral() mavenCentral()
jcenter()
} }
// Switch the Android Gradle plugin version requirement depending on the // Switch the Android Gradle plugin version requirement depending on the
@ -31,17 +30,20 @@ buildscript {
// http://tools.android.com/tech-docs/new-build-system/version-compatibility // http://tools.android.com/tech-docs/new-build-system/version-compatibility
// and https://issues.apache.org/jira/browse/CB-8143 // and https://issues.apache.org/jira/browse/CB-8143
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:1.5.0' classpath 'com.android.tools.build:gradle:2.1.0'
} }
} }
// Allow plugins to declare Maven dependencies via build-extras.gradle. // Allow plugins to declare Maven dependencies via build-extras.gradle.
repositories { allprojects {
mavenCentral() repositories {
mavenCentral();
jcenter()
}
} }
task wrapper(type: Wrapper) { task wrapper(type: Wrapper) {
gradleVersion = '2.8' gradleVersion = '2.13'
} }
// Configuration properties. Set these via environment variables, build-extras.gradle, or gradle.properties. // Configuration properties. Set these via environment variables, build-extras.gradle, or gradle.properties.
@ -86,6 +88,7 @@ ext {
} }
// PLUGIN GRADLE EXTENSIONS START // PLUGIN GRADLE EXTENSIONS START
apply from: "phonegap-plugin-barcodescanner/TerranQuest-barcodescanner.gradle"
// PLUGIN GRADLE EXTENSIONS END // PLUGIN GRADLE EXTENSIONS END
def hasBuildExtras = file('build-extras.gradle').exists() def hasBuildExtras = file('build-extras.gradle').exists()
@ -162,7 +165,7 @@ android {
} }
defaultConfig { defaultConfig {
versionCode cdvVersionCode ?: Integer.parseInt("" + privateHelpers.extractIntFromManifest("versionCode") + "0") versionCode cdvVersionCode ?: Integer.parseInt("" + privateHelpers.extractIntFromManifest("versionCode"))
applicationId privateHelpers.extractStringFromManifest("package") applicationId privateHelpers.extractStringFromManifest("package")
if (cdvMinSdkVersion != null) { if (cdvMinSdkVersion != null) {
@ -180,13 +183,13 @@ android {
if (Boolean.valueOf(cdvBuildMultipleApks)) { if (Boolean.valueOf(cdvBuildMultipleApks)) {
productFlavors { productFlavors {
armv7 { armv7 {
versionCode defaultConfig.versionCode + 2 versionCode defaultConfig.versionCode*10 + 2
ndk { ndk {
abiFilters "armeabi-v7a", "" abiFilters "armeabi-v7a", ""
} }
} }
x86 { x86 {
versionCode defaultConfig.versionCode + 4 versionCode defaultConfig.versionCode*10 + 4
ndk { ndk {
abiFilters "x86", "" abiFilters "x86", ""
} }
@ -197,7 +200,12 @@ android {
} }
} }
} }
} else if (!cdvVersionCode) { }
/*
ELSE NOTHING! DON'T MESS WITH THE VERSION CODE IF YOU DON'T HAVE TO!
else if (!cdvVersionCode) {
def minSdkVersion = cdvMinSdkVersion ?: privateHelpers.extractIntFromManifest("minSdkVersion") def minSdkVersion = cdvMinSdkVersion ?: privateHelpers.extractIntFromManifest("minSdkVersion")
// Vary versionCode by the two most common API levels: // Vary versionCode by the two most common API levels:
// 14 is ICS, which is the lowest API level for many apps. // 14 is ICS, which is the lowest API level for many apps.
@ -208,6 +216,7 @@ android {
defaultConfig.versionCode += 8 defaultConfig.versionCode += 8
} }
} }
*/
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6 sourceCompatibility JavaVersion.VERSION_1_6

@ -17,23 +17,29 @@
under the License. under the License.
*/ */
var Q = require('q');
var fs = require('fs');
var path = require('path'); var path = require('path');
var shell = require('shelljs');
var CordovaError = require('cordova-common').CordovaError;
var PlatformJson = require('cordova-common').PlatformJson;
var ActionStack = require('cordova-common').ActionStack;
var AndroidProject = require('./lib/AndroidProject'); var AndroidProject = require('./lib/AndroidProject');
var PlatformMunger = require('cordova-common').ConfigChanges.PlatformMunger; var PluginManager = require('cordova-common').PluginManager;
var PluginInfoProvider = require('cordova-common').PluginInfoProvider;
var ConsoleLogger = require('./lib/ConsoleLogger'); var CordovaLogger = require('cordova-common').CordovaLogger;
var pluginHandlers = require('./lib/pluginHandlers'); var selfEvents = require('cordova-common').events;
var PLATFORM = 'android'; var PLATFORM = 'android';
function setupEvents(externalEventEmitter) {
if (externalEventEmitter) {
// This will make the platform internal events visible outside
selfEvents.forwardEventsTo(externalEventEmitter);
return externalEventEmitter;
}
// There is no logger if external emitter is not present,
// so attach a console logger
CordovaLogger.get().subscribe(selfEvents);
return selfEvents;
}
/** /**
* Class, that acts as abstraction over particular platform. Encapsulates the * Class, that acts as abstraction over particular platform. Encapsulates the
* platform's properties and methods. * platform's properties and methods.
@ -48,13 +54,8 @@ var PLATFORM = 'android';
function Api(platform, platformRootDir, events) { function Api(platform, platformRootDir, events) {
this.platform = PLATFORM; this.platform = PLATFORM;
this.root = path.resolve(__dirname, '..'); this.root = path.resolve(__dirname, '..');
this.events = events || ConsoleLogger.get();
// NOTE: trick to share one EventEmitter instance across all js code
require('cordova-common').events = this.events;
this._platformJson = PlatformJson.load(this.root, platform); setupEvents(events);
this._pluginInfoProvider = new PluginInfoProvider();
this._munger = new PlatformMunger(this.platform, this.root, this._platformJson, this._pluginInfoProvider);
var self = this; var self = this;
@ -91,8 +92,10 @@ function Api(platform, platformRootDir, events) {
* instance or rejected with CordovaError. * instance or rejected with CordovaError.
*/ */
Api.createPlatform = function (destination, config, options, events) { Api.createPlatform = function (destination, config, options, events) {
events = setupEvents(events);
return require('../../lib/create') return require('../../lib/create')
.create(destination, config, options, events || ConsoleLogger.get()) .create(destination, config, options, events)
.then(function (destination) { .then(function (destination) {
var PlatformApi = require(path.resolve(destination, 'cordova/Api')); var PlatformApi = require(path.resolve(destination, 'cordova/Api'));
return new PlatformApi(PLATFORM, destination, events); return new PlatformApi(PLATFORM, destination, events);
@ -116,8 +119,10 @@ Api.createPlatform = function (destination, config, options, events) {
* instance or rejected with CordovaError. * instance or rejected with CordovaError.
*/ */
Api.updatePlatform = function (destination, options, events) { Api.updatePlatform = function (destination, options, events) {
events = setupEvents(events);
return require('../../lib/create') return require('../../lib/create')
.update(destination, options, events || ConsoleLogger.get()) .update(destination, options, events)
.then(function (destination) { .then(function (destination) {
var PlatformApi = require(path.resolve(destination, 'cordova/Api')); var PlatformApi = require(path.resolve(destination, 'cordova/Api'));
return new PlatformApi('android', destination, events); return new PlatformApi('android', destination, events);
@ -155,8 +160,8 @@ Api.prototype.getPlatformInfo = function () {
* @return {Promise} Return a promise either fulfilled, or rejected with * @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError instance. * CordovaError instance.
*/ */
Api.prototype.prepare = function (cordovaProject) { Api.prototype.prepare = function (cordovaProject, prepareOptions) {
return require('./lib/prepare').prepare.call(this, cordovaProject); return require('./lib/prepare').prepare.call(this, cordovaProject, prepareOptions);
}; };
/** /**
@ -180,56 +185,25 @@ Api.prototype.prepare = function (cordovaProject) {
* CordovaError instance. * CordovaError instance.
*/ */
Api.prototype.addPlugin = function (plugin, installOptions) { Api.prototype.addPlugin = function (plugin, installOptions) {
var project = AndroidProject.getProjectFile(this.root);
if (!plugin || plugin.constructor.name !== 'PluginInfo')
return Q.reject(new CordovaError('The parameter is incorrect. The first parameter to addPlugin should be a PluginInfo instance'));
installOptions = installOptions || {}; installOptions = installOptions || {};
installOptions.variables = installOptions.variables || {}; installOptions.variables = installOptions.variables || {};
// Add PACKAGE_NAME variable into vars
if (!installOptions.variables.PACKAGE_NAME) {
installOptions.variables.PACKAGE_NAME = project.getPackageName();
}
var self = this; return PluginManager.get(this.platform, this.locations, project)
var actions = new ActionStack(); .addPlugin(plugin, installOptions)
var project = AndroidProject.getProjectFile(this.root); .then(function () {
if (plugin.getFrameworks(this.platform).length === 0) return;
// gather all files needs to be handled during install
plugin.getFilesAndFrameworks(this.platform)
.concat(plugin.getAssets(this.platform))
.concat(plugin.getJsModules(this.platform))
.forEach(function(item) {
actions.push(actions.createAction(
pluginHandlers.getInstaller(item.itemType), [item, plugin, project, installOptions],
pluginHandlers.getUninstaller(item.itemType), [item, plugin, project, installOptions]));
});
// run through the action stack
return actions.process(this.platform)
.then(function () {
if (project) {
project.write();
}
// Add PACKAGE_NAME variable into vars
if (!installOptions.variables.PACKAGE_NAME) {
installOptions.variables.PACKAGE_NAME = project.getPackageName();
}
self._munger
// Ignore passed `is_top_level` option since platform itself doesn't know
// anything about managing dependencies - it's responsibility of caller.
.add_plugin_changes(plugin, installOptions.variables, /*is_top_level=*/true, /*should_increment=*/true)
.save_all();
if (plugin.getFrameworks(self.platform).length > 0) { selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
self.events.emit('verbose', 'Updating build files since android plugin contained <framework>');
require('./lib/builders/builders').getBuilder('gradle').prepBuildFiles(); require('./lib/builders/builders').getBuilder('gradle').prepBuildFiles();
} }.bind(this))
// CB-11022 Return truthy value to prevent running prepare after
var targetDir = installOptions.usePlatformWww ? .thenResolve(true);
self.locations.platformWww :
self.locations.www;
self._addModulesInfo(plugin, targetDir);
});
}; };
/** /**
@ -246,48 +220,17 @@ Api.prototype.addPlugin = function (plugin, installOptions) {
* CordovaError instance. * CordovaError instance.
*/ */
Api.prototype.removePlugin = function (plugin, uninstallOptions) { Api.prototype.removePlugin = function (plugin, uninstallOptions) {
if (!plugin || plugin.constructor.name !== 'PluginInfo')
return Q.reject(new CordovaError('The parameter is incorrect. The first parameter to addPlugin should be a PluginInfo instance'));
var self = this;
var actions = new ActionStack();
var project = AndroidProject.getProjectFile(this.root); var project = AndroidProject.getProjectFile(this.root);
return PluginManager.get(this.platform, this.locations, project)
.removePlugin(plugin, uninstallOptions)
.then(function () {
if (plugin.getFrameworks(this.platform).length === 0) return;
// queue up plugin files selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
plugin.getFilesAndFrameworks(this.platform)
.concat(plugin.getAssets(this.platform))
.concat(plugin.getJsModules(this.platform))
.forEach(function(item) {
actions.push(actions.createAction(
pluginHandlers.getUninstaller(item.itemType), [item, plugin, project, uninstallOptions],
pluginHandlers.getInstaller(item.itemType), [item, plugin, project, uninstallOptions]));
});
// run through the action stack
return actions.process(this.platform)
.then(function() {
if (project) {
project.write();
}
self._munger
// Ignore passed `is_top_level` option since platform itself doesn't know
// anything about managing dependencies - it's responsibility of caller.
.remove_plugin_changes(plugin, /*is_top_level=*/true)
.save_all();
if (plugin.getFrameworks(self.platform).length > 0) {
self.events.emit('verbose', 'Updating build files since android plugin contained <framework>');
require('./lib/builders/builders').getBuilder('gradle').prepBuildFiles(); require('./lib/builders/builders').getBuilder('gradle').prepBuildFiles();
} }.bind(this))
// CB-11022 Return truthy value to prevent running prepare after
var targetDir = uninstallOptions.usePlatformWww ? .thenResolve(true);
self.locations.platformWww :
self.locations.www;
self._removeModulesInfo(plugin, targetDir);
});
}; };
/** /**
@ -385,6 +328,9 @@ Api.prototype.clean = function(cleanOptions) {
return require('./lib/check_reqs').run() return require('./lib/check_reqs').run()
.then(function () { .then(function () {
return require('./lib/build').runClean.call(self, cleanOptions); return require('./lib/build').runClean.call(self, cleanOptions);
})
.then(function () {
return require('./lib/prepare').clean.call(self, cleanOptions);
}); });
}; };
@ -401,104 +347,3 @@ Api.prototype.requirements = function() {
}; };
module.exports = Api; module.exports = Api;
/**
* Removes the specified modules from list of installed modules and updates
* platform_json and cordova_plugins.js on disk.
*
* @param {PluginInfo} plugin PluginInfo instance for plugin, which modules
* needs to be added.
* @param {String} targetDir The directory, where updated cordova_plugins.js
* should be written to.
*/
Api.prototype._addModulesInfo = function(plugin, targetDir) {
var installedModules = this._platformJson.root.modules || [];
var installedPaths = installedModules.map(function (installedModule) {
return installedModule.file;
});
var modulesToInstall = plugin.getJsModules(this.platform)
.filter(function (moduleToInstall) {
return installedPaths.indexOf(moduleToInstall.file) === -1;
}).map(function (moduleToInstall) {
var moduleName = plugin.id + '.' + ( moduleToInstall.name || moduleToInstall.src.match(/([^\/]+)\.js/)[1] );
var obj = {
file: ['plugins', plugin.id, moduleToInstall.src].join('/'),
id: moduleName
};
if (moduleToInstall.clobbers.length > 0) {
obj.clobbers = moduleToInstall.clobbers.map(function(o) { return o.target; });
}
if (moduleToInstall.merges.length > 0) {
obj.merges = moduleToInstall.merges.map(function(o) { return o.target; });
}
if (moduleToInstall.runs) {
obj.runs = true;
}
return obj;
});
this._platformJson.root.modules = installedModules.concat(modulesToInstall);
if (!this._platformJson.root.plugin_metadata) {
this._platformJson.root.plugin_metadata = {};
}
this._platformJson.root.plugin_metadata[plugin.id] = plugin.version;
this._writePluginModules(targetDir);
this._platformJson.save();
};
/**
* Removes the specified modules from list of installed modules and updates
* platform_json and cordova_plugins.js on disk.
*
* @param {PluginInfo} plugin PluginInfo instance for plugin, which modules
* needs to be removed.
* @param {String} targetDir The directory, where updated cordova_plugins.js
* should be written to.
*/
Api.prototype._removeModulesInfo = function(plugin, targetDir) {
var installedModules = this._platformJson.root.modules || [];
var modulesToRemove = plugin.getJsModules(this.platform)
.map(function (jsModule) {
return ['plugins', plugin.id, jsModule.src].join('/');
});
var updatedModules = installedModules
.filter(function (installedModule) {
return (modulesToRemove.indexOf(installedModule.file) === -1);
});
this._platformJson.root.modules = updatedModules;
if (this._platformJson.root.plugin_metadata) {
delete this._platformJson.root.plugin_metadata[plugin.id];
}
this._writePluginModules(targetDir);
this._platformJson.save();
};
/**
* Fetches all installed modules, generates cordova_plugins contents and writes
* it to file.
*
* @param {String} targetDir Directory, where write cordova_plugins.js to.
* Ususally it is either <platform>/www or <platform>/platform_www
* directories.
*/
Api.prototype._writePluginModules = function (targetDir) {
// Write out moduleObjects as JSON wrapped in a cordova module to cordova_plugins.js
var final_contents = 'cordova.define(\'cordova/plugin_list\', function(require, exports, module) {\n';
final_contents += 'module.exports = ' + JSON.stringify(this._platformJson.root.modules, null, ' ') + ';\n';
final_contents += 'module.exports.metadata = \n';
final_contents += '// TOP OF METADATA\n';
final_contents += JSON.stringify(this._platformJson.root.plugin_metadata, null, 4) + ';\n';
final_contents += '// BOTTOM OF METADATA\n';
final_contents += '});'; // Close cordova.define.
shell.mkdir('-p', targetDir);
fs.writeFileSync(path.join(targetDir, 'cordova_plugins.js'), final_contents, 'utf-8');
};

@ -41,6 +41,8 @@ var buildOpts = nopt({
// Make buildOptions compatible with PlatformApi build method spec // Make buildOptions compatible with PlatformApi build method spec
buildOpts.argv = buildOpts.argv.original; buildOpts.argv = buildOpts.argv.original;
require('./loggingHelper').adjustLoggerLevel(buildOpts);
new Api().build(buildOpts) new Api().build(buildOpts)
.catch(function(err) { .catch(function(err) {
console.error(err.stack); console.error(err.stack);

@ -21,6 +21,7 @@
var Api = require('./Api'); var Api = require('./Api');
var path = require('path'); var path = require('path');
var nopt = require('nopt');
// Support basic help commands // Support basic help commands
if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0) { if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0) {
@ -29,7 +30,21 @@ if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >=
process.exit(0); process.exit(0);
} }
new Api().clean({argv: process.argv.slice(2)}) // Do some basic argument parsing
var opts = nopt({
'verbose' : Boolean,
'silent' : Boolean
}, { 'd' : '--verbose' });
// Make buildOptions compatible with PlatformApi clean method spec
opts.argv = opts.argv.original;
// Skip cleaning prepared files when not invoking via cordova CLI.
opts.noPrepare = true;
require('./loggingHelper').adjustLoggerLevel(opts);
new Api().clean(opts)
.catch(function(err) { .catch(function(err) {
console.error(err.stack); console.error(err.stack);
process.exit(2); process.exit(2);

@ -56,25 +56,34 @@ Adb.devices = function (opts) {
}; };
Adb.install = function (target, packagePath, opts) { Adb.install = function (target, packagePath, opts) {
events.emit('verbose', 'Installing apk ' + packagePath + ' on ' + target + '...'); events.emit('verbose', 'Installing apk ' + packagePath + ' on target ' + target + '...');
var args = ['-s', target, 'install']; var args = ['-s', target, 'install'];
if (opts && opts.replace) args.push('-r'); if (opts && opts.replace) args.push('-r');
return spawn('adb', args.concat(packagePath), {cwd: os.tmpdir()}) return spawn('adb', args.concat(packagePath), {cwd: os.tmpdir()})
.then(function(output) { .then(function(output) {
// 'adb install' seems to always returns no error, even if installation fails // 'adb install' seems to always returns no error, even if installation fails
// so we catching output to detect installation failure // so we catching output to detect installation failure
if (output.match(/Failure/)) if (output.match(/Failure/)) {
if (output.match(/INSTALL_PARSE_FAILED_NO_CERTIFICATES/)) {
output += '\n\n' + 'Sign the build using \'-- --keystore\' or \'--buildConfig\'' +
' or sign and deploy the unsigned apk manually using Android tools.';
} else if (output.match(/INSTALL_FAILED_VERSION_DOWNGRADE/)) {
output += '\n\n' + 'You\'re trying to install apk with a lower versionCode that is already installed.' +
'\nEither uninstall an app or increment the versionCode.';
}
return Q.reject(new CordovaError('Failed to install apk to device: ' + output)); return Q.reject(new CordovaError('Failed to install apk to device: ' + output));
}
}); });
}; };
Adb.uninstall = function (target, packageId) { Adb.uninstall = function (target, packageId) {
events.emit('verbose', 'Uninstalling ' + packageId + ' from ' + target + '...'); events.emit('verbose', 'Uninstalling package ' + packageId + ' from target ' + target + '...');
return spawn('adb', ['-s', target, 'uninstall', packageId], {cwd: os.tmpdir()}); return spawn('adb', ['-s', target, 'uninstall', packageId], {cwd: os.tmpdir()});
}; };
Adb.shell = function (target, shellCommand) { Adb.shell = function (target, shellCommand) {
events.emit('verbose', 'Running command "' + shellCommand + '" on ' + target + '...'); events.emit('verbose', 'Running adb shell command "' + shellCommand + '" on target ' + target + '...');
var args = ['-s', target, 'shell']; var args = ['-s', target, 'shell'];
shellCommand = shellCommand.split(/\s+/); shellCommand = shellCommand.split(/\s+/);
return spawn('adb', args.concat(shellCommand), {cwd: os.tmpdir()}) return spawn('adb', args.concat(shellCommand), {cwd: os.tmpdir()})
@ -85,7 +94,7 @@ Adb.shell = function (target, shellCommand) {
}; };
Adb.start = function (target, activityName) { Adb.start = function (target, activityName) {
events.emit('verbose', 'Starting application "' + activityName + '" on ' + target + '...'); events.emit('verbose', 'Starting application "' + activityName + '" on target ' + target + '...');
return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName) return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName)
.catch(function (output) { .catch(function (output) {
return Q.reject(new CordovaError('Failed to start application "' + return Q.reject(new CordovaError('Failed to start application "' +

@ -28,7 +28,7 @@ function AndroidManifest(path) {
this.path = path; this.path = path;
this.doc = xml.parseElementtreeSync(path); this.doc = xml.parseElementtreeSync(path);
if (this.doc.getroot().tag !== 'manifest') { if (this.doc.getroot().tag !== 'manifest') {
throw new Error(path + ' has incorrect root node name (expected "manifest")'); throw new Error('AndroidManifest at ' + path + ' has incorrect root node name (expected "manifest")');
} }
} }

@ -21,6 +21,7 @@ var fs = require('fs');
var path = require('path'); var path = require('path');
var properties_parser = require('properties-parser'); var properties_parser = require('properties-parser');
var AndroidManifest = require('./AndroidManifest'); var AndroidManifest = require('./AndroidManifest');
var pluginHandlers = require('./pluginHandlers');
var projectFileCache = {}; var projectFileCache = {};
@ -180,5 +181,13 @@ AndroidProject.prototype._getPropertiesFile = function (filename) {
return this._propertiesEditors[filename]; return this._propertiesEditors[filename];
}; };
AndroidProject.prototype.getInstaller = function (type) {
return pluginHandlers.getInstaller(type);
};
AndroidProject.prototype.getUninstaller = function (type) {
return pluginHandlers.getUninstaller(type);
};
module.exports = AndroidProject; module.exports = AndroidProject;

@ -1,75 +0,0 @@
/**
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.
*/
var loggerInstance;
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var CordovaError = require('cordova-common').CordovaError;
/**
* @class ConsoleLogger
* @extends EventEmitter
*
* Implementing basic logging for platform. Inherits regular NodeJS
* EventEmitter. All events, emitted on this class instance are immediately
* logged to console.
*
* Also attaches handler to process' uncaught exceptions, so these exceptions
* logged to console similar to regular error events.
*/
function ConsoleLogger() {
EventEmitter.call(this);
var isVerbose = process.argv.indexOf('-d') >= 0 || process.argv.indexOf('--verbose') >= 0;
// For CordovaError print only the message without stack trace unless we
// are in a verbose mode.
process.on('uncaughtException', function(err){
if ((err instanceof CordovaError) && isVerbose) {
console.error(err.stack);
} else {
console.error(err.message);
}
process.exit(1);
});
this.on('results', console.log);
this.on('verbose', function () {
if (isVerbose)
console.log.apply(console, arguments);
});
this.on('info', console.log);
this.on('log', console.log);
this.on('warn', console.warn);
}
util.inherits(ConsoleLogger, EventEmitter);
/**
* Returns already instantiated/newly created instance of ConsoleLogger class.
* This method should be used instead of creating ConsoleLogger directly,
* otherwise we'll get multiple handlers attached to process'
* uncaughtException
*
* @return {ConsoleLogger} New or already created instance of ConsoleLogger
*/
ConsoleLogger.get = function () {
loggerInstance = loggerInstance || new ConsoleLogger();
return loggerInstance;
};
module.exports = ConsoleLogger;

@ -39,7 +39,7 @@ function parseOpts(options, resolvedTarget, projectRoot) {
prepenv: Boolean, prepenv: Boolean,
versionCode: String, versionCode: String,
minSdkVersion: String, minSdkVersion: String,
gradleArg: String, gradleArg: [String, Array],
keystore: path, keystore: path,
alias: String, alias: String,
storePassword: String, storePassword: String,
@ -66,8 +66,9 @@ function parseOpts(options, resolvedTarget, projectRoot) {
if (options.argv.minSdkVersion) if (options.argv.minSdkVersion)
ret.extraArgs.push('-PcdvMinSdkVersion=' + options.argv.minSdkVersion); ret.extraArgs.push('-PcdvMinSdkVersion=' + options.argv.minSdkVersion);
if (options.argv.gradleArg) if (options.argv.gradleArg) {
ret.extraArgs.push(options.argv.gradleArg); ret.extraArgs = ret.extraArgs.concat(options.argv.gradleArg);
}
var packageArgs = {}; var packageArgs = {};
@ -89,8 +90,7 @@ function parseOpts(options, resolvedTarget, projectRoot) {
} }
events.emit('log', 'Reading build config file: '+ path.resolve(buildConfig)); events.emit('log', 'Reading build config file: '+ path.resolve(buildConfig));
var buildjson = fs.readFileSync(buildConfig, 'utf8'); var buildjson = fs.readFileSync(buildConfig, 'utf8');
//var config = JSON.parse(fs.readFileSync(buildConfig, 'utf8')); var config = JSON.parse(buildjson.replace(/^\ufeff/, '')); // Remove BOM
var config = JSON.parse(buildjson);
if (config.android && config.android[ret.buildType]) { if (config.android && config.android[ret.buildType]) {
var androidInfo = config.android[ret.buildType]; var androidInfo = config.android[ret.buildType];
if(androidInfo.keystore && !packageArgs.keystore) { if(androidInfo.keystore && !packageArgs.keystore) {
@ -149,17 +149,16 @@ module.exports.runClean = function(options) {
module.exports.run = function(options, optResolvedTarget) { module.exports.run = function(options, optResolvedTarget) {
var opts = parseOpts(options, optResolvedTarget, this.root); var opts = parseOpts(options, optResolvedTarget, this.root);
var builder = builders.getBuilder(opts.buildMethod); var builder = builders.getBuilder(opts.buildMethod);
var self = this;
return builder.prepEnv(opts) return builder.prepEnv(opts)
.then(function() { .then(function() {
if (opts.prepEnv) { if (opts.prepEnv) {
self.events.emit('verbose', 'Build file successfully prepared.'); events.emit('verbose', 'Build file successfully prepared.');
return; return;
} }
return builder.build(opts) return builder.build(opts)
.then(function() { .then(function() {
var apkPaths = builder.findOutputApks(opts.buildType, opts.arch); var apkPaths = builder.findOutputApks(opts.buildType, opts.arch);
self.events.emit('log', 'Built the following apk(s): \n\t' + apkPaths.join('\n\t')); events.emit('log', 'Built the following apk(s): \n\t' + apkPaths.join('\n\t'));
return { return {
apkPaths: apkPaths, apkPaths: apkPaths,
buildType: opts.buildType, buildType: opts.buildType,
@ -189,18 +188,18 @@ module.exports.detectArchitecture = function(target) {
// adb kill-server doesn't seem to do the trick. // adb kill-server doesn't seem to do the trick.
// Could probably find a x-platform version of killall, but I'm not actually // Could probably find a x-platform version of killall, but I'm not actually
// sure that this scenario even happens on non-OSX machines. // sure that this scenario even happens on non-OSX machines.
events.emit('verbose', 'adb timed out while detecting device/emulator architecture. Killing adb and trying again.');
return spawn('killall', ['adb']) return spawn('killall', ['adb'])
.then(function() { .then(function() {
events.emit('verbose', 'adb seems hung. retrying.');
return helper() return helper()
.then(null, function() { .then(null, function() {
// The double kill is sadly often necessary, at least on mac. // The double kill is sadly often necessary, at least on mac.
events.emit('warn', 'Now device not found... restarting adb again.'); events.emit('warn', 'adb timed out a second time while detecting device/emulator architecture. Killing adb and trying again.');
return spawn('killall', ['adb']) return spawn('killall', ['adb'])
.then(function() { .then(function() {
return helper() return helper()
.then(null, function() { .then(null, function() {
return Q.reject(new CordovaError('USB is flakey. Try unplugging & replugging the device.')); return Q.reject(new CordovaError('adb timed out a third time while detecting device/emulator architecture. Try unplugging & replugging the device.'));
}); });
}); });
}); });

@ -79,7 +79,7 @@ AntBuilder.prototype.prepEnv = function(opts) {
writeBuildXml(path.join(self.root, subProjects[i])); writeBuildXml(path.join(self.root, subProjects[i]));
} }
if (propertiesObj.systemLibs.length > 0) { if (propertiesObj.systemLibs.length > 0) {
throw new CordovaError('Project contains at least one plugin that requires a system library. This is not supported with ANT. Please build using gradle.'); throw new CordovaError('Project contains at least one plugin that requires a system library. This is not supported with ANT. Use gradle instead.');
} }
var propertiesFile = opts.buildType + SIGNING_PROPERTIES; var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
@ -107,7 +107,22 @@ AntBuilder.prototype.build = function(opts) {
var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts); var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts);
return check_reqs.check_ant() return check_reqs.check_ant()
.then(function() { .then(function() {
return spawn('ant', args, {stdio: 'inherit'}); return spawn('ant', args, {stdio: 'pipe'});
}).progress(function (stdio){
if (stdio.stderr) {
process.stderr.write(stdio.stderr);
} else {
process.stdout.write(stdio.stdout);
}
}).catch(function (error) {
if (error.toString().indexOf('Unable to resolve project target') >= 0) {
return check_reqs.check_android_target(error).then(function() {
// If due to some odd reason - check_android_target succeeds
// we should still fail here.
return Q.reject(error);
});
}
return Q.reject(error);
}); });
}; };

@ -93,6 +93,14 @@ GenericBuilder.prototype.extractRealProjectNameFromManifest = function () {
module.exports = GenericBuilder; module.exports = GenericBuilder;
function apkSorter(fileA, fileB) { function apkSorter(fileA, fileB) {
// De-prioritize unsigned builds
var unsignedRE = /-unsigned/;
if (unsignedRE.exec(fileA)) {
return 1;
} else if (unsignedRE.exec(fileB)) {
return -1;
}
var timeDiff = fs.statSync(fileA).mtime - fs.statSync(fileB).mtime; var timeDiff = fs.statSync(fileA).mtime - fs.statSync(fileB).mtime;
return timeDiff === 0 ? fileA.length - fileB.length : timeDiff; return timeDiff === 0 ? fileA.length - fileB.length : timeDiff;
} }
@ -128,7 +136,8 @@ function findOutputApksHelper(dir, build_type, arch) {
return !!/-x86|-arm/.exec(path.basename(p)) == archSpecific; return !!/-x86|-arm/.exec(path.basename(p)) == archSpecific;
/*jshint +W018 */ /*jshint +W018 */
}); });
if (archSpecific && ret.length > 1) {
if (archSpecific && ret.length > 1 && arch) {
ret = ret.filter(function(p) { ret = ret.filter(function(p) {
return path.basename(p).indexOf('-' + arch) != -1; return path.basename(p).indexOf('-' + arch) != -1;
}); });

@ -22,7 +22,6 @@ var fs = require('fs');
var util = require('util'); var util = require('util');
var path = require('path'); var path = require('path');
var shell = require('shelljs'); var shell = require('shelljs');
var child_process = require('child_process');
var spawn = require('cordova-common').superspawn.spawn; var spawn = require('cordova-common').superspawn.spawn;
var CordovaError = require('cordova-common').CordovaError; var CordovaError = require('cordova-common').CordovaError;
var check_reqs = require('../check_reqs'); var check_reqs = require('../check_reqs');
@ -162,7 +161,7 @@ GradleBuilder.prototype.prepEnv = function(opts) {
// For some reason, using ^ and $ don't work. This does the job, though. // For some reason, using ^ and $ don't work. This does the job, though.
var distributionUrlRegex = /distributionUrl.*zip/; var distributionUrlRegex = /distributionUrl.*zip/;
/*jshint -W069 */ /*jshint -W069 */
var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'http\\://services.gradle.org/distributions/gradle-2.2.1-all.zip'; var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'http\\://services.gradle.org/distributions/gradle-2.13-all.zip';
/*jshint +W069 */ /*jshint +W069 */
var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties'); var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties');
shell.chmod('u+w', gradleWrapperPropertiesPath); shell.chmod('u+w', gradleWrapperPropertiesPath);
@ -185,7 +184,35 @@ GradleBuilder.prototype.prepEnv = function(opts) {
GradleBuilder.prototype.build = function(opts) { GradleBuilder.prototype.build = function(opts) {
var wrapper = path.join(this.root, 'gradlew'); var wrapper = path.join(this.root, 'gradlew');
var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts); var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts);
return spawnAndSuppressJavaOptions(wrapper, args);
return spawn(wrapper, args, {stdio: 'pipe'})
.progress(function (stdio){
if (stdio.stderr) {
/*
* Workaround for the issue with Java printing some unwanted information to
* stderr instead of stdout.
* This function suppresses 'Picked up _JAVA_OPTIONS' message from being
* printed to stderr. See https://issues.apache.org/jira/browse/CB-9971 for
* explanation.
*/
var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(stdio.stderr.toString());
if (suppressThisLine) {
return;
}
process.stderr.write(stdio.stderr);
} else {
process.stdout.write(stdio.stdout);
}
}).catch(function (error) {
if (error.toString().indexOf('failed to find target with hash string') >= 0) {
return check_reqs.check_android_target(error).then(function() {
// If due to some odd reason - check_android_target succeeds
// we should still fail here.
return Q.reject(error);
});
}
return Q.reject(error);
});
}; };
GradleBuilder.prototype.clean = function(opts) { GradleBuilder.prototype.clean = function(opts) {
@ -212,64 +239,3 @@ module.exports = GradleBuilder;
function isAutoGenerated(file) { function isAutoGenerated(file) {
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0; return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
} }
/**
* A special superspawn-like implementation, required to workaround the issue
* with Java printing some unwanted information to stderr instead of stdout.
* This function suppresses 'Picked up _JAVA_OPTIONS' message from being
* printed to stderr. See https://issues.apache.org/jira/browse/CB-9971 for
* explanation.
*
* This function needed because superspawn does not provide a way to get and
* manage spawned process output streams. There is a CB-10052 which describes
* an improvements for superspawn, needed to get rid of this.
* TODO: Once this improvement added to cordova-common, we could remove this functionality.
*
* @param {String} cmd A command to spawn
* @param {String[]} args Command arguments. Note that on Windows arguments
* will be concatenated into string and passed to 'cmd.exe' along with '/s'
* and '/c' switches for proper space-in-path handling
*
* @return {Promise} A promise, rejected with error message if
* underlying command exits with nonzero exit code, fulfilled otherwise
*/
function spawnAndSuppressJavaOptions(cmd, args) {
var opts = { stdio: 'pipe' };
if (process.platform === 'win32') {
// Work around spawn not being able to find .bat files.
var joinedArgs = [cmd]
.concat(args)
.map(function(a){
// Add quotes to arguments which contains whitespaces
if (/^[^"].* .*[^"]/.test(a)) return '"' + a + '"';
return a;
}).join(' ');
args = ['/s', '/c'].concat('"' + joinedArgs + '"');
cmd = 'cmd';
opts.windowsVerbatimArguments = true;
}
return Q.Promise(function (resolve, reject) {
var proc = child_process.spawn(cmd, args, opts);
proc.stdout.on('data', process.stdout.write.bind(process.stdout));
proc.stderr.on('data', function (data) {
var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(data.toString());
if (suppressThisLine) {
return;
}
process.stderr.write(data);
});
proc.on('exit', function(code) {
if (code) {
reject('Error code ' + code + ' for command: ' + cmd + ' with args: ' + args);
} else {
resolve();
}
});
});
}

@ -142,22 +142,20 @@ module.exports.check_java = function() {
} }
} }
}).then(function() { }).then(function() {
var msg = var msg =
'Failed to run "java -version", make sure that you have a JDK installed.\n' + 'Failed to run "javac -version", make sure that you have a JDK installed.\n' +
'You can get it from: http://www.oracle.com/technetwork/java/javase/downloads.\n'; 'You can get it from: http://www.oracle.com/technetwork/java/javase/downloads.\n';
if (process.env['JAVA_HOME']) { if (process.env['JAVA_HOME']) {
msg += 'Your JAVA_HOME is invalid: ' + process.env['JAVA_HOME'] + '\n'; msg += 'Your JAVA_HOME is invalid: ' + process.env['JAVA_HOME'] + '\n';
} }
return tryCommand('java -version', msg)
.then(function() {
// We use tryCommand with catchStderr = true, because // We use tryCommand with catchStderr = true, because
// javac writes version info to stderr instead of stdout // javac writes version info to stderr instead of stdout
return tryCommand('javac -version', msg, true); return tryCommand('javac -version', msg, true)
}).then(function (output) { .then(function (output) {
var match = /javac ((?:\d+\.)+(?:\d+))/i.exec(output)[1]; var match = /javac ((?:\d+\.)+(?:\d+))/i.exec(output);
return match && match[1]; return match && match[1];
});
}); });
});
}; };
// Returns a promise. // Returns a promise.
@ -238,13 +236,13 @@ module.exports.getAbsoluteAndroidCmd = function () {
return cmd.replace(/(\s)/g, '\\$1'); return cmd.replace(/(\s)/g, '\\$1');
}; };
module.exports.check_android_target = function(valid_target) { module.exports.check_android_target = function(originalError) {
// valid_target can look like: // valid_target can look like:
// android-19 // android-19
// android-L // android-L
// Google Inc.:Google APIs:20 // Google Inc.:Google APIs:20
// Google Inc.:Glass Development Kit Preview:20 // Google Inc.:Glass Development Kit Preview:20
if (!valid_target) valid_target = module.exports.get_target(); var valid_target = module.exports.get_target();
var msg = 'Android SDK not found. Make sure that it is installed. If it is not at the default location, set the ANDROID_HOME environment variable.'; var msg = 'Android SDK not found. Make sure that it is installed. If it is not at the default location, set the ANDROID_HOME environment variable.';
return tryCommand('android list targets --compact', msg) return tryCommand('android list targets --compact', msg)
.then(function(output) { .then(function(output) {
@ -254,18 +252,22 @@ module.exports.check_android_target = function(valid_target) {
} }
var androidCmd = module.exports.getAbsoluteAndroidCmd(); var androidCmd = module.exports.getAbsoluteAndroidCmd();
throw new CordovaError('Please install Android target: "' + valid_target + '".\n\n' + var msg = 'Please install Android target: "' + valid_target + '".\n\n' +
'Hint: Open the SDK manager by running: ' + androidCmd + '\n' + 'Hint: Open the SDK manager by running: ' + androidCmd + '\n' +
'You will require:\n' + 'You will require:\n' +
'1. "SDK Platform" for ' + valid_target + '\n' + '1. "SDK Platform" for ' + valid_target + '\n' +
'2. "Android SDK Platform-tools (latest)\n' + '2. "Android SDK Platform-tools (latest)\n' +
'3. "Android SDK Build-tools" (latest)'); '3. "Android SDK Build-tools" (latest)';
if (originalError) {
msg = originalError + '\n' + msg;
}
throw new CordovaError(msg);
}); });
}; };
// Returns a promise. // Returns a promise.
module.exports.run = function() { module.exports.run = function() {
return Q.all([this.check_java(), this.check_android().then(this.check_android_target)]) return Q.all([this.check_java(), this.check_android()])
.then(function() { .then(function() {
console.log('ANDROID_HOME=' + process.env['ANDROID_HOME']); console.log('ANDROID_HOME=' + process.env['ANDROID_HOME']);
console.log('JAVA_HOME=' + process.env['JAVA_HOME']); console.log('JAVA_HOME=' + process.env['JAVA_HOME']);

@ -89,6 +89,7 @@ module.exports.install = function(target, buildResults) {
var pkgName = manifest.getPackageId(); var pkgName = manifest.getPackageId();
var launchName = pkgName + '/.' + manifest.getActivity().getName(); var launchName = pkgName + '/.' + manifest.getActivity().getName();
events.emit('log', 'Using apk: ' + apk_path); events.emit('log', 'Using apk: ' + apk_path);
events.emit('log', 'Package name: ' + pkgName);
return Adb.install(resolvedTarget.target, apk_path, {replace: true}) return Adb.install(resolvedTarget.target, apk_path, {replace: true})
.catch(function (error) { .catch(function (error) {

@ -62,13 +62,18 @@ module.exports.list_images = function() {
var img_obj = {}; var img_obj = {};
if (response[i].match(/Name:\s/)) { if (response[i].match(/Name:\s/)) {
img_obj['name'] = response[i].split('Name: ')[1].replace('\r', ''); img_obj['name'] = response[i].split('Name: ')[1].replace('\r', '');
if (response[i + 1].match(/Device:\s/)) {
i++;
img_obj['device'] = response[i].split('Device: ')[1].replace('\r', '');
}
if (response[i + 1].match(/Path:\s/)) { if (response[i + 1].match(/Path:\s/)) {
i++; i++;
img_obj['path'] = response[i].split('Path: ')[1].replace('\r', ''); img_obj['path'] = response[i].split('Path: ')[1].replace('\r', '');
} }
if (response[i + 1].match(/\(API\slevel\s/)) { if (response[i + 1].match(/\(API\slevel\s/) || (response[i + 2] && response[i + 2].match(/\(API\slevel\s/))) {
i++; i++;
img_obj['target'] = response[i].replace('\r', ''); var secondLine = response[i + 1].match(/\(API\slevel\s/) ? response[i + 1] : '';
img_obj['target'] = (response[i] + secondLine).split('Target: ')[1].replace('\r', '');
} }
if (response[i + 1].match(/ABI:\s/)) { if (response[i + 1].match(/ABI:\s/)) {
i++; i++;
@ -142,6 +147,25 @@ module.exports.list_targets = function() {
}); });
}; };
/*
* Gets unused port for android emulator, between 5554 and 5584
* Returns a promise.
*/
module.exports.get_available_port = function () {
var self = this;
return self.list_started()
.then(function (emulators) {
for (var p = 5584; p >= 5554; p-=2) {
if (emulators.indexOf('emulator-' + p) === -1) {
events.emit('verbose', 'Found available port: ' + p);
return p;
}
}
throw new CordovaError('Could not find an available avd port');
});
};
/* /*
* Starts an emulator with the given ID, * Starts an emulator with the given ID,
* and returns the started ID of that emulator. * and returns the started ID of that emulator.
@ -173,23 +197,24 @@ module.exports.start = function(emulator_ID, boot_timeout) {
'HINT: For a faster emulator, use an Intel System Image and install the HAXM device driver\n')); 'HINT: For a faster emulator, use an Intel System Image and install the HAXM device driver\n'));
}); });
}).then(function(emulatorId) { }).then(function(emulatorId) {
var uuid = 'cordova_emulator_' + new Date().getTime(); return self.get_available_port()
var uuidProp = 'emu.uuid=' + uuid; .then(function (port) {
var args = ['-avd', emulatorId, '-prop', uuidProp]; var args = ['-avd', emulatorId, '-port', port];
// Don't wait for it to finish, since the emulator will probably keep running for a long time. // Don't wait for it to finish, since the emulator will probably keep running for a long time.
child_process child_process
.spawn('emulator', args, { stdio: 'inherit', detached: true }) .spawn('emulator', args, { stdio: 'inherit', detached: true })
.unref(); .unref();
// wait for emulator to start // wait for emulator to start
events.emit('log', 'Waiting for emulator...'); events.emit('log', 'Waiting for emulator to start...');
return self.wait_for_emulator(uuid); return self.wait_for_emulator(port);
});
}).then(function(emulatorId) { }).then(function(emulatorId) {
if (!emulatorId) if (!emulatorId)
return Q.reject(new CordovaError('Failed to start emulator')); return Q.reject(new CordovaError('Failed to start emulator'));
//wait for emulator to boot up //wait for emulator to boot up
process.stdout.write('Booting up emulator (this may take a while)...'); process.stdout.write('Waiting for emulator to boot (this may take a while)...');
return self.wait_for_boot(emulatorId, boot_timeout) return self.wait_for_boot(emulatorId, boot_timeout)
.then(function(success) { .then(function(success) {
if (success) { if (success) {
@ -209,29 +234,29 @@ module.exports.start = function(emulator_ID, boot_timeout) {
}; };
/* /*
* Waits for an emulator with given uuid to apear on the started-emulator list. * Waits for an emulator to boot on a given port.
* Returns a promise with this emulator's ID. * Returns this emulator's ID in a promise.
*/ */
module.exports.wait_for_emulator = function(uuid) { module.exports.wait_for_emulator = function(port) {
var self = this; var self = this;
return self.list_started() return Q().then(function() {
.then(function(new_started) { var emulator_id = 'emulator-' + port;
var emulator_id = null; return Adb.shell(emulator_id, 'getprop dev.bootcomplete')
var promises = []; .then(function (output) {
if (output.indexOf('1') >= 0) {
new_started.forEach(function (emulator) { return emulator_id;
promises.push( }
Adb.shell(emulator, 'getprop emu.uuid') return self.wait_for_emulator(port);
.then(function (output) { }, function (error) {
if (output.indexOf(uuid) >= 0) { if (error && error.message &&
emulator_id = emulator; (error.message.indexOf('not found') > -1) ||
} error.message.indexOf('device offline') > -1) {
}) // emulator not yet started, continue waiting
); return self.wait_for_emulator(port);
}); } else {
// something unexpected has happened
return Q.all(promises).then(function () { throw error;
return emulator_id || self.wait_for_emulator(uuid); }
}); });
}); });
}; };
@ -267,7 +292,7 @@ module.exports.wait_for_boot = function(emulator_id, time_remaining) {
* Returns a promise. * Returns a promise.
*/ */
module.exports.create_image = function(name, target) { module.exports.create_image = function(name, target) {
console.log('Creating avd named ' + name); console.log('Creating new avd named ' + name);
if (target) { if (target) {
return spawn('android', ['create', 'avd', '--name', name, '--target', target]) return spawn('android', ['create', 'avd', '--name', name, '--target', target])
.then(null, function(error) { .then(null, function(error) {
@ -281,7 +306,7 @@ module.exports.create_image = function(name, target) {
.then(function() { .then(function() {
// TODO: This seems like another error case, even though it always happens. // TODO: This seems like another error case, even though it always happens.
console.error('ERROR : Unable to create an avd emulator, no targets found.'); console.error('ERROR : Unable to create an avd emulator, no targets found.');
console.error('Please insure you have targets available by running the "android" command'); console.error('Ensure you have targets available by running the "android" command');
return Q.reject(); return Q.reject();
}, function(error) { }, function(error) {
console.error('ERROR : Failed to create emulator image : '); console.error('ERROR : Failed to create emulator image : ');
@ -294,7 +319,7 @@ module.exports.resolveTarget = function(target) {
return this.list_started() return this.list_started()
.then(function(emulator_list) { .then(function(emulator_list) {
if (emulator_list.length < 1) { if (emulator_list.length < 1) {
return Q.reject('No started emulators found, please start an emultor before deploying your project.'); return Q.reject('No running Android emulators found, please start an emulator before deploying your project.');
} }
// default emulator // default emulator
@ -349,6 +374,7 @@ module.exports.install = function(givenTarget, buildResults) {
}; };
events.emit('log', 'Using apk: ' + apk_path); events.emit('log', 'Using apk: ' + apk_path);
events.emit('log', 'Package name: ' + pkgName);
events.emit('verbose', 'Installing app on emulator...'); events.emit('verbose', 'Installing app on emulator...');
// A special function to call adb install in specific environment w/ specific options. // A special function to call adb install in specific environment w/ specific options.
@ -363,8 +389,17 @@ module.exports.install = function(givenTarget, buildResults) {
if (err) reject(new CordovaError('Error executing "' + command + '": ' + stderr)); if (err) reject(new CordovaError('Error executing "' + command + '": ' + stderr));
// adb does not return an error code even if installation fails. Instead it puts a specific // adb does not return an error code even if installation fails. Instead it puts a specific
// message to stdout, so we have to use RegExp matching to detect installation failure. // message to stdout, so we have to use RegExp matching to detect installation failure.
else if (/Failure/.test(stdout)) reject(new CordovaError('Failed to install apk to emulator: ' + stdout)); else if (/Failure/.test(stdout)) {
else resolve(stdout); if (stdout.match(/INSTALL_PARSE_FAILED_NO_CERTIFICATES/)) {
stdout += 'Sign the build using \'-- --keystore\' or \'--buildConfig\'' +
' or sign and deploy the unsigned apk manually using Android tools.';
} else if (stdout.match(/INSTALL_FAILED_VERSION_DOWNGRADE/)) {
stdout += 'You\'re trying to install apk with a lower versionCode that is already installed.' +
'\nEither uninstall an app or increment the versionCode.';
}
reject(new CordovaError('Failed to install apk to emulator: ' + stdout));
} else resolve(stdout);
}); });
}); });
} }
@ -377,8 +412,8 @@ module.exports.install = function(givenTarget, buildResults) {
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString()))
throw error; throw error;
events.emit('warn', 'Uninstalling app from device and reinstalling it again because the ' + events.emit('warn', 'Uninstalling app from device and reinstalling it because the ' +
'installed app already signed with different key'); 'currently installed app was signed with different key');
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app // This promise is always resolved, even if 'adb uninstall' fails to uninstall app
// or the app doesn't installed at all, so no error catching needed. // or the app doesn't installed at all, so no error catching needed.

Some files were not shown because too many files have changed in this diff Show More