MAID: Fix build errors, update to Ionic 4 and Angular 5
@ -34,5 +34,6 @@ DynamicBibleIonic/platforms/android/build
|
||||
DynamicBibleIonic/platforms/android/CordovaLib/build
|
||||
DynamicBibleIonic/platforms/android/.gradle
|
||||
DynamicBibleIonic/platforms/android/debug-signing.properties
|
||||
DynamicBibleIonic/platforms/android/app
|
||||
syntax: regexp
|
||||
npm-debug.log.*
|
||||
|
1
DynamicBibleIonic/.sourcemaps/main.js.map
Normal file
@ -79,5 +79,5 @@
|
||||
<plugin name="cordova-plugin-statusbar" spec="^2.2.3" />
|
||||
<plugin name="cordova-plugin-whitelist" spec="^1.3.1" />
|
||||
<plugin name="ionic-plugin-keyboard" spec="^2.2.1" />
|
||||
<engine name="android" spec="^6.2.3" />
|
||||
<engine name="android" spec="7.0.0" />
|
||||
</widget>
|
||||
|
11703
DynamicBibleIonic/package-lock.json
generated
Normal file
@ -19,37 +19,39 @@
|
||||
"test-coverage": "ng test --code-coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/cli": "1.3.1",
|
||||
"@angular/common": "4.1.3",
|
||||
"@angular/compiler": "4.1.3",
|
||||
"@angular/compiler-cli": "4.1.3",
|
||||
"@angular/core": "4.1.3",
|
||||
"@angular/forms": "4.1.3",
|
||||
"@angular/http": "4.1.3",
|
||||
"@angular/platform-browser": "4.1.3",
|
||||
"@angular/platform-browser-dynamic": "4.1.3",
|
||||
"@ionic-native/core": "3.12.1",
|
||||
"@ionic-native/splash-screen": "3.12.1",
|
||||
"@ionic-native/status-bar": "3.12.1",
|
||||
"@ionic/storage": "2.0.1",
|
||||
"angularfire2": "^5.0.0-rc.3",
|
||||
"cordova-android": "^6.2.3",
|
||||
"cordova-plugin-console": "^1.0.5",
|
||||
"@angular/cli": "1.6.3",
|
||||
"@angular/common": "5.0.0",
|
||||
"@angular/compiler": "5.0.0",
|
||||
"@angular/compiler-cli": "5.0.0",
|
||||
"@angular/core": "5.0.0",
|
||||
"@angular/forms": "5.0.0",
|
||||
"@angular/http": "5.0.0",
|
||||
"@angular/platform-browser": "5.0.0",
|
||||
"@angular/platform-browser-dynamic": "5.0.0",
|
||||
"@ionic-native/core": "4.3.1",
|
||||
"@ionic-native/splash-screen": "4.3.1",
|
||||
"@ionic-native/status-bar": "4.3.1",
|
||||
"@ionic/storage": "2.1.3",
|
||||
"angularfire2": "^5.0.0-rc.4",
|
||||
"cordova-android": "7.0.0",
|
||||
"cordova-plugin-console": "^1.1.0",
|
||||
"cordova-plugin-crosswalk-webview": "^2.3.0",
|
||||
"cordova-plugin-device": "^1.1.4",
|
||||
"cordova-plugin-splashscreen": "^4.0.3",
|
||||
"cordova-plugin-statusbar": "^2.2.3",
|
||||
"cordova-plugin-whitelist": "^1.3.1",
|
||||
"ionic-angular": "3.6.0",
|
||||
"cordova-plugin-device": "^1.1.7",
|
||||
"cordova-plugin-splashscreen": "^4.1.0",
|
||||
"cordova-plugin-statusbar": "^2.4.1",
|
||||
"cordova-plugin-whitelist": "^1.3.3",
|
||||
"firebase": "^4.8.0",
|
||||
"ionic-angular": "3.9.0",
|
||||
"ionic-plugin-keyboard": "^2.2.1",
|
||||
"ionicons": "3.0.0",
|
||||
"rxjs": "5.4.0",
|
||||
"promise-polyfill": "^6.0.2",
|
||||
"rxjs": "5.5.2",
|
||||
"sw-toolbox": "3.6.0",
|
||||
"zone.js": "0.8.12"
|
||||
"zone.js": "0.8.18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/cli": "1.1.2",
|
||||
"@ionic/app-scripts": "2.1.0",
|
||||
"@ionic/app-scripts": "3.1.0",
|
||||
"@types/jasmine": "2.5.41",
|
||||
"@types/node": "7.0.4",
|
||||
"codecov": "2.2.0",
|
||||
@ -69,7 +71,8 @@
|
||||
"ts-node": "3.0.4",
|
||||
"tslint": "5.3.2",
|
||||
"tslint-eslint-rules": "4.1.1",
|
||||
"typescript": "2.3.4"
|
||||
"typescript": "~2.4.2",
|
||||
"ws": "3.3.2"
|
||||
},
|
||||
"cordovaPlugins": [
|
||||
"cordova-plugin-whitelist",
|
||||
|
@ -1,16 +0,0 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<manifest android:hardwareAccelerated="true" android:versionCode="30003" android:versionName="3.0.3" package="walljm.dynamicbible" 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" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<application android:hardwareAccelerated="true" android:icon="@mipmap/icon" android:label="@string/app_name" android:supportsRtl="true">
|
||||
<activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale" android:label="@string/activity_name" android:launchMode="singleTop" android:name="MainActivity" android:theme="@android:style/Theme.DeviceDefault.NoActionBar" android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter android:label="@string/launcher_name">
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="25" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
</manifest>
|
@ -19,5 +19,5 @@
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.apache.cordova" android:versionName="1.0" android:versionCode="1">
|
||||
<uses-sdk android:minSdkVersion="14" />
|
||||
<uses-sdk android:minSdkVersion="16" />
|
||||
</manifest>
|
||||
|
@ -24,12 +24,14 @@ ext {
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
maven {
|
||||
url "https://maven.google.com"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.2.3'
|
||||
classpath 'com.android.tools.build:gradle:3.0.0'
|
||||
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
|
||||
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3'
|
||||
}
|
||||
@ -40,7 +42,7 @@ apply plugin: 'com.github.dcendents.android-maven'
|
||||
apply plugin: 'com.jfrog.bintray'
|
||||
|
||||
group = 'org.apache.cordova'
|
||||
version = '6.2.3'
|
||||
version = '7.0.0'
|
||||
|
||||
android {
|
||||
compileSdkVersion cdvCompileSdkVersion
|
||||
@ -48,8 +50,8 @@ android {
|
||||
publishNonDefault true
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_6
|
||||
targetCompatibility JavaVersion.VERSION_1_6
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@ -127,9 +129,9 @@ bintray {
|
||||
licenses = ['Apache-2.0']
|
||||
labels = ['android', 'cordova', 'phonegap']
|
||||
version {
|
||||
name = '6.2.3'
|
||||
name = '7.0.0'
|
||||
released = new Date()
|
||||
vcsTag = '6.2.3'
|
||||
vcsTag = '7.0.0'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,11 @@ String doEnsureValueExists(filePath, props, key) {
|
||||
|
||||
String doGetProjectTarget() {
|
||||
def props = new Properties()
|
||||
file('project.properties').withReader { reader ->
|
||||
def propertiesFile = 'project.properties';
|
||||
if(!(file(propertiesFile).exists())) {
|
||||
propertiesFile = '../project.properties';
|
||||
}
|
||||
file(propertiesFile).withReader { reader ->
|
||||
props.load(reader)
|
||||
}
|
||||
return doEnsureValueExists('project.properties', props, 'target')
|
||||
|
@ -10,7 +10,7 @@
|
||||
# Indicates whether an apk should be generated for each density.
|
||||
split.density=false
|
||||
# Project target.
|
||||
target=android-25
|
||||
target=android-26
|
||||
apk-configurations=
|
||||
renderscript.opt.level=O0
|
||||
android.library=true
|
||||
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
/*
|
||||
* This is a utility class that allows us to get the BuildConfig variable, which is required
|
||||
* for the use of different providers. This is not guaranteed to work, and it's better for this
|
||||
* to be set in the build step in config.xml
|
||||
*
|
||||
*/
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
|
||||
public class BuildHelper {
|
||||
|
||||
|
||||
private static String TAG="BuildHelper";
|
||||
|
||||
/*
|
||||
* This needs to be implemented if you wish to use the Camera Plugin or other plugins
|
||||
* that read the Build Configuration.
|
||||
*
|
||||
* Thanks to Phil@Medtronic and Graham Borland for finding the answer and posting it to
|
||||
* StackOverflow. This is annoying as hell! However, this method does not work with
|
||||
* ProGuard, and you should use the config.xml to define the application_id
|
||||
*
|
||||
*/
|
||||
|
||||
public static Object getBuildConfigValue(Context ctx, String key)
|
||||
{
|
||||
try
|
||||
{
|
||||
Class<?> clazz = Class.forName(ctx.getPackageName() + ".BuildConfig");
|
||||
Field field = clazz.getField(key);
|
||||
return field.get(null);
|
||||
} catch (ClassNotFoundException e) {
|
||||
LOG.d(TAG, "Unable to get the BuildConfig, is this built with ANT?");
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchFieldException e) {
|
||||
LOG.d(TAG, key + " is not a valid field. Check your build.gradle");
|
||||
} catch (IllegalAccessException e) {
|
||||
LOG.d(TAG, "Illegal Access Exception: Let's print a stack trace.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -319,6 +319,7 @@ public class CordovaActivity extends Activity {
|
||||
/**
|
||||
* Called when view focus is changed
|
||||
*/
|
||||
@SuppressLint("InlinedApi")
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
|
@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import org.json.JSONArray;
|
||||
@ -110,6 +112,9 @@ public class CordovaBridge {
|
||||
}
|
||||
|
||||
/** Called by cordova.js to initialize the bridge. */
|
||||
//On old Androids SecureRandom isn't really secure, this is the least of your problems if
|
||||
//you're running Android 4.3 and below in 2017
|
||||
@SuppressLint("TrulyRandom")
|
||||
int generateBridgeSecret() {
|
||||
SecureRandom randGen = new SecureRandom();
|
||||
expectedBridgeSecret = randGen.nextInt(Integer.MAX_VALUE);
|
||||
|
@ -22,10 +22,12 @@ import java.security.Principal;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.webkit.ClientCertRequest;
|
||||
|
||||
/**
|
||||
* Implementation of the ICordovaClientCertRequest for Android WebView.
|
||||
*
|
||||
*/
|
||||
public class CordovaClientCertRequest implements ICordovaClientCertRequest {
|
||||
|
||||
@ -38,6 +40,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
|
||||
/**
|
||||
* Cancel this request
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
public void cancel()
|
||||
{
|
||||
request.cancel();
|
||||
@ -46,6 +49,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
|
||||
/*
|
||||
* Returns the host name of the server requesting the certificate.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
public String getHost()
|
||||
{
|
||||
return request.getHost();
|
||||
@ -54,6 +58,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
|
||||
/*
|
||||
* Returns the acceptable types of asymmetric keys (can be null).
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
public String[] getKeyTypes()
|
||||
{
|
||||
return request.getKeyTypes();
|
||||
@ -62,6 +67,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
|
||||
/*
|
||||
* Returns the port number of the server requesting the certificate.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
public int getPort()
|
||||
{
|
||||
return request.getPort();
|
||||
@ -70,6 +76,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
|
||||
/*
|
||||
* Returns the acceptable certificate issuers for the certificate matching the private key (can be null).
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
public Principal[] getPrincipals()
|
||||
{
|
||||
return request.getPrincipals();
|
||||
@ -78,6 +85,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
|
||||
/*
|
||||
* Ignore the request for now. Do not remember user's choice.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
public void ignore()
|
||||
{
|
||||
request.ignore();
|
||||
@ -89,6 +97,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
|
||||
* @param privateKey The privateKey
|
||||
* @param chain The certificate chain
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
public void proceed(PrivateKey privateKey, X509Certificate[] chain)
|
||||
{
|
||||
request.proceed(privateKey, chain);
|
||||
|
@ -19,6 +19,7 @@
|
||||
package org.apache.cordova;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
@ -51,10 +52,18 @@ public interface CordovaInterface {
|
||||
/**
|
||||
* Get the Android activity.
|
||||
*
|
||||
* If a custom engine lives outside of the Activity's lifecycle the return value may be null.
|
||||
*
|
||||
* @return the Activity
|
||||
*/
|
||||
public abstract Activity getActivity();
|
||||
|
||||
|
||||
/**
|
||||
* Get the Android context.
|
||||
*
|
||||
* @return the Context
|
||||
*/
|
||||
public Context getContext();
|
||||
|
||||
/**
|
||||
* Called when a message is sent to plugin.
|
||||
|
@ -19,7 +19,9 @@
|
||||
|
||||
package org.apache.cordova;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
@ -84,6 +86,11 @@ public class CordovaInterfaceImpl implements CordovaInterface {
|
||||
return activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getContext() {
|
||||
return activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onMessage(String id, Object data) {
|
||||
if ("exit".equals(id)) {
|
||||
@ -221,6 +228,7 @@ public class CordovaInterfaceImpl implements CordovaInterface {
|
||||
requestPermissions(plugin, requestCode, permissions);
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public void requestPermissions(CordovaPlugin plugin, int requestCode, String [] permissions) {
|
||||
int mappedRequestCode = permissionResultCallbacks.registerCallback(plugin, requestCode);
|
||||
getActivity().requestPermissions(permissions, mappedRequestCode);
|
||||
|
@ -31,7 +31,7 @@ import android.webkit.WebChromeClient.CustomViewCallback;
|
||||
* are not expected to implement it.
|
||||
*/
|
||||
public interface CordovaWebView {
|
||||
public static final String CORDOVA_VERSION = "6.2.3";
|
||||
public static final String CORDOVA_VERSION = "7.0.0";
|
||||
|
||||
void init(CordovaInterface cordova, List<PluginEntry> pluginEntries, CordovaPreferences preferences);
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
@ -91,6 +92,7 @@ public class CordovaWebViewImpl implements CordovaWebView {
|
||||
init(cordova, new ArrayList<PluginEntry>(), new CordovaPreferences());
|
||||
}
|
||||
|
||||
@SuppressLint("Assert")
|
||||
@Override
|
||||
public void init(CordovaInterface cordova, List<PluginEntry> pluginEntries, CordovaPreferences preferences) {
|
||||
if (this.cordova != null) {
|
||||
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
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 java.util.Arrays;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
/**
|
||||
* This class provides reflective methods for permission requesting and checking so that plugins
|
||||
* written for cordova-android 5.0.0+ can still compile with earlier cordova-android versions.
|
||||
*/
|
||||
public class PermissionHelper {
|
||||
private static final String LOG_TAG = "CordovaPermissionHelper";
|
||||
|
||||
/**
|
||||
* Requests a "dangerous" permission for the application at runtime. This is a helper method
|
||||
* alternative to cordovaInterface.requestPermission() that does not require the project to be
|
||||
* built with cordova-android 5.0.0+
|
||||
*
|
||||
* @param plugin The plugin the permission is being requested for
|
||||
* @param requestCode A requestCode to be passed to the plugin's onRequestPermissionResult()
|
||||
* along with the result of the permission request
|
||||
* @param permission The permission to be requested
|
||||
*/
|
||||
public static void requestPermission(CordovaPlugin plugin, int requestCode, String permission) {
|
||||
PermissionHelper.requestPermissions(plugin, requestCode, new String[] {permission});
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests "dangerous" permissions for the application at runtime. This is a helper method
|
||||
* alternative to cordovaInterface.requestPermissions() that does not require the project to be
|
||||
* built with cordova-android 5.0.0+
|
||||
*
|
||||
* @param plugin The plugin the permissions are being requested for
|
||||
* @param requestCode A requestCode to be passed to the plugin's onRequestPermissionResult()
|
||||
* along with the result of the permissions request
|
||||
* @param permissions The permissions to be requested
|
||||
*/
|
||||
public static void requestPermissions(CordovaPlugin plugin, int requestCode, String[] permissions) {
|
||||
plugin.cordova.requestPermissions(plugin, requestCode, permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks at runtime to see if the application has been granted a permission. This is a helper
|
||||
* method alternative to cordovaInterface.hasPermission() that does not require the project to
|
||||
* be built with cordova-android 5.0.0+
|
||||
*
|
||||
* @param plugin The plugin the permission is being checked against
|
||||
* @param permission The permission to be checked
|
||||
*
|
||||
* @return True if the permission has already been granted and false otherwise
|
||||
*/
|
||||
public static boolean hasPermission(CordovaPlugin plugin, String permission) {
|
||||
return plugin.cordova.hasPermission(permission);
|
||||
}
|
||||
|
||||
private static void deliverPermissionResult(CordovaPlugin plugin, int requestCode, String[] permissions) {
|
||||
// Generate the request results
|
||||
int[] requestResults = new int[permissions.length];
|
||||
Arrays.fill(requestResults, PackageManager.PERMISSION_GRANTED);
|
||||
|
||||
try {
|
||||
plugin.onRequestPermissionResult(requestCode, permissions, requestResults);
|
||||
} catch (JSONException e) {
|
||||
LOG.e(LOG_TAG, "JSONException when delivering permissions results", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -110,7 +110,11 @@ public class SystemWebViewEngine implements CordovaWebViewEngine {
|
||||
nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode.OnlineEventsBridgeModeDelegate() {
|
||||
@Override
|
||||
public void setNetworkAvailable(boolean value) {
|
||||
webView.setNetworkAvailable(value);
|
||||
//sometimes this can be called after calling webview.destroy() on destroy()
|
||||
//thus resulting in a NullPointerException
|
||||
if(webView!=null) {
|
||||
webView.setNetworkAvailable(value);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void runOnUiThread(Runnable r) {
|
||||
@ -210,11 +214,6 @@ public class SystemWebViewEngine implements CordovaWebViewEngine {
|
||||
settings.setAppCachePath(databasePath);
|
||||
settings.setAppCacheEnabled(true);
|
||||
|
||||
// Enable scaling
|
||||
// Fix for CB-12015
|
||||
settings.setUseWideViewPort(true);
|
||||
settings.setLoadWithOverviewMode(true);
|
||||
|
||||
// Fix for CB-1405
|
||||
// Google issue 4641
|
||||
String defaultUserAgent = settings.getUserAgentString();
|
||||
@ -255,6 +254,9 @@ public class SystemWebViewEngine implements CordovaWebViewEngine {
|
||||
}
|
||||
}
|
||||
|
||||
// Yeah, we know, which is why we makes ure that we don't do this if the bridge is
|
||||
// below JELLYBEAN_MR1. It'd be great if lint was just a little smarter.
|
||||
@SuppressLint("AddJavascriptInterface")
|
||||
private static void exposeJsInterface(WebView webView, CordovaBridge bridge) {
|
||||
if ((Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)) {
|
||||
LOG.i(TAG, "Disabled addJavascriptInterface() bridge since Android version is old.");
|
||||
|
@ -1,151 +1,151 @@
|
||||
{
|
||||
"prepare_queue": {
|
||||
"installed": [],
|
||||
"uninstalled": []
|
||||
},
|
||||
"config_munge": {
|
||||
"files": {
|
||||
"res/xml/config.xml": {
|
||||
"parents": {
|
||||
"/*": [
|
||||
{
|
||||
"xml": "<feature name=\"Device\"><param name=\"android-package\" value=\"org.apache.cordova.device.Device\" /></feature>",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<feature name=\"SplashScreen\"><param name=\"android-package\" value=\"org.apache.cordova.splashscreen.SplashScreen\" /><param name=\"onload\" value=\"true\" /></feature>",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<feature name=\"StatusBar\"><param name=\"android-package\" value=\"org.apache.cordova.statusbar.StatusBar\" /><param name=\"onload\" value=\"true\" /></feature>",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<feature name=\"Whitelist\"><param name=\"android-package\" value=\"org.apache.cordova.whitelist.WhitelistPlugin\" /><param name=\"onload\" value=\"true\" /></feature>",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<feature name=\"Keyboard\"><param name=\"android-package\" value=\"io.ionic.keyboard.IonicKeyboard\" /><param name=\"onload\" value=\"true\" /></feature>",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<preference name=\"webView\" value=\"org.crosswalk.engine.XWalkWebViewEngine\" />",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<preference name=\"xwalkVersion\" value=\"23+\" />",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<preference name=\"xwalkLiteVersion\" value=\"xwalk_core_library_canary:17+\" />",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<preference name=\"xwalkCommandLine\" value=\"--disable-pull-to-refresh-effect\" />",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<preference name=\"xwalkMode\" value=\"embedded\" />",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<preference name=\"xwalkMultipleApk\" value=\"true\" />",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<preference name=\"android-minSdkVersion\" value=\"16\" />",
|
||||
"count": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
"prepare_queue": {
|
||||
"installed": [],
|
||||
"uninstalled": []
|
||||
},
|
||||
"config_munge": {
|
||||
"files": {
|
||||
"res/xml/config.xml": {
|
||||
"parents": {
|
||||
"/*": [
|
||||
{
|
||||
"xml": "<preference name=\"webView\" value=\"org.crosswalk.engine.XWalkWebViewEngine\" />",
|
||||
"count": 1
|
||||
},
|
||||
"AndroidManifest.xml": {
|
||||
"parents": {
|
||||
"/*": [
|
||||
{
|
||||
"xml": "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />",
|
||||
"count": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"xml": "<preference name=\"xwalkVersion\" value=\"23+\" />",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<preference name=\"xwalkLiteVersion\" value=\"xwalk_core_library_canary:17+\" />",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<preference name=\"xwalkCommandLine\" value=\"--disable-pull-to-refresh-effect\" />",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<preference name=\"xwalkMode\" value=\"embedded\" />",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<preference name=\"xwalkMultipleApk\" value=\"true\" />",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<preference name=\"android-minSdkVersion\" value=\"16\" />",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<feature name=\"Device\"><param name=\"android-package\" value=\"org.apache.cordova.device.Device\" /></feature>",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<feature name=\"SplashScreen\"><param name=\"android-package\" value=\"org.apache.cordova.splashscreen.SplashScreen\" /><param name=\"onload\" value=\"true\" /></feature>",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<feature name=\"StatusBar\"><param name=\"android-package\" value=\"org.apache.cordova.statusbar.StatusBar\" /><param name=\"onload\" value=\"true\" /></feature>",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<feature name=\"Whitelist\"><param name=\"android-package\" value=\"org.apache.cordova.whitelist.WhitelistPlugin\" /><param name=\"onload\" value=\"true\" /></feature>",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<feature name=\"Keyboard\"><param name=\"android-package\" value=\"io.ionic.keyboard.IonicKeyboard\" /><param name=\"onload\" value=\"true\" /></feature>",
|
||||
"count": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"installed_plugins": {
|
||||
"cordova-plugin-console": {
|
||||
"PACKAGE_NAME": "walljm.dynamicbible"
|
||||
},
|
||||
"cordova-plugin-device": {
|
||||
"PACKAGE_NAME": "walljm.dynamicbible"
|
||||
},
|
||||
"cordova-plugin-splashscreen": {
|
||||
"PACKAGE_NAME": "walljm.dynamicbible"
|
||||
},
|
||||
"cordova-plugin-statusbar": {
|
||||
"PACKAGE_NAME": "walljm.dynamicbible"
|
||||
},
|
||||
"cordova-plugin-whitelist": {
|
||||
"PACKAGE_NAME": "walljm.dynamicbible"
|
||||
},
|
||||
"ionic-plugin-keyboard": {
|
||||
"PACKAGE_NAME": "walljm.dynamicbible"
|
||||
},
|
||||
"cordova-plugin-crosswalk-webview": {
|
||||
"XWALK_VERSION": "23+",
|
||||
"XWALK_LITEVERSION": "xwalk_core_library_canary:17+",
|
||||
"XWALK_COMMANDLINE": "--disable-pull-to-refresh-effect",
|
||||
"XWALK_MODE": "embedded",
|
||||
"XWALK_MULTIPLEAPK": "true",
|
||||
"PACKAGE_NAME": "walljm.dynamicbible"
|
||||
},
|
||||
"AndroidManifest.xml": {
|
||||
"parents": {
|
||||
"/*": [
|
||||
{
|
||||
"xml": "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />",
|
||||
"count": 1
|
||||
},
|
||||
{
|
||||
"xml": "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />",
|
||||
"count": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"dependent_plugins": {},
|
||||
"modules": [
|
||||
{
|
||||
"id": "cordova-plugin-device.device",
|
||||
"file": "plugins/cordova-plugin-device/www/device.js",
|
||||
"pluginId": "cordova-plugin-device",
|
||||
"clobbers": [
|
||||
"device"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cordova-plugin-splashscreen.SplashScreen",
|
||||
"file": "plugins/cordova-plugin-splashscreen/www/splashscreen.js",
|
||||
"pluginId": "cordova-plugin-splashscreen",
|
||||
"clobbers": [
|
||||
"navigator.splashscreen"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cordova-plugin-statusbar.statusbar",
|
||||
"file": "plugins/cordova-plugin-statusbar/www/statusbar.js",
|
||||
"pluginId": "cordova-plugin-statusbar",
|
||||
"clobbers": [
|
||||
"window.StatusBar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ionic-plugin-keyboard.keyboard",
|
||||
"file": "plugins/ionic-plugin-keyboard/www/android/keyboard.js",
|
||||
"pluginId": "ionic-plugin-keyboard",
|
||||
"clobbers": [
|
||||
"cordova.plugins.Keyboard"
|
||||
],
|
||||
"runs": true
|
||||
}
|
||||
],
|
||||
"plugin_metadata": {
|
||||
"cordova-plugin-console": "1.0.5",
|
||||
"cordova-plugin-device": "1.1.4",
|
||||
"cordova-plugin-splashscreen": "4.0.3",
|
||||
"cordova-plugin-statusbar": "2.2.3",
|
||||
"cordova-plugin-whitelist": "1.3.1",
|
||||
"ionic-plugin-keyboard": "2.2.1",
|
||||
"cordova-plugin-crosswalk-webview": "2.3.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"installed_plugins": {
|
||||
"cordova-plugin-console": {
|
||||
"PACKAGE_NAME": "walljm.dynamicbible"
|
||||
},
|
||||
"cordova-plugin-crosswalk-webview": {
|
||||
"XWALK_VERSION": "23+",
|
||||
"XWALK_LITEVERSION": "xwalk_core_library_canary:17+",
|
||||
"XWALK_COMMANDLINE": "--disable-pull-to-refresh-effect",
|
||||
"XWALK_MODE": "embedded",
|
||||
"XWALK_MULTIPLEAPK": "true",
|
||||
"PACKAGE_NAME": "walljm.dynamicbible"
|
||||
},
|
||||
"cordova-plugin-device": {
|
||||
"PACKAGE_NAME": "walljm.dynamicbible"
|
||||
},
|
||||
"cordova-plugin-splashscreen": {
|
||||
"PACKAGE_NAME": "walljm.dynamicbible"
|
||||
},
|
||||
"cordova-plugin-statusbar": {
|
||||
"PACKAGE_NAME": "walljm.dynamicbible"
|
||||
},
|
||||
"cordova-plugin-whitelist": {
|
||||
"PACKAGE_NAME": "walljm.dynamicbible"
|
||||
},
|
||||
"ionic-plugin-keyboard": {
|
||||
"PACKAGE_NAME": "walljm.dynamicbible"
|
||||
}
|
||||
},
|
||||
"dependent_plugins": {},
|
||||
"modules": [
|
||||
{
|
||||
"id": "cordova-plugin-device.device",
|
||||
"file": "plugins/cordova-plugin-device/www/device.js",
|
||||
"pluginId": "cordova-plugin-device",
|
||||
"clobbers": [
|
||||
"device"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cordova-plugin-splashscreen.SplashScreen",
|
||||
"file": "plugins/cordova-plugin-splashscreen/www/splashscreen.js",
|
||||
"pluginId": "cordova-plugin-splashscreen",
|
||||
"clobbers": [
|
||||
"navigator.splashscreen"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cordova-plugin-statusbar.statusbar",
|
||||
"file": "plugins/cordova-plugin-statusbar/www/statusbar.js",
|
||||
"pluginId": "cordova-plugin-statusbar",
|
||||
"clobbers": [
|
||||
"window.StatusBar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ionic-plugin-keyboard.keyboard",
|
||||
"file": "plugins/ionic-plugin-keyboard/www/android/keyboard.js",
|
||||
"pluginId": "ionic-plugin-keyboard",
|
||||
"clobbers": [
|
||||
"cordova.plugins.Keyboard"
|
||||
],
|
||||
"runs": true
|
||||
}
|
||||
],
|
||||
"plugin_metadata": {
|
||||
"cordova-plugin-console": "1.1.0",
|
||||
"cordova-plugin-crosswalk-webview": "2.3.0",
|
||||
"cordova-plugin-device": "1.1.7",
|
||||
"cordova-plugin-splashscreen": "4.1.0",
|
||||
"cordova-plugin-statusbar": "2.4.1",
|
||||
"cordova-plugin-whitelist": "1.3.3",
|
||||
"ionic-plugin-keyboard": "2.2.1"
|
||||
}
|
||||
}
|
@ -1,314 +1,54 @@
|
||||
/*
|
||||
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
|
||||
/* 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
|
||||
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.
|
||||
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.
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
maven {
|
||||
url "https://maven.google.com"
|
||||
}
|
||||
}
|
||||
|
||||
// Switch the Android Gradle plugin version requirement depending on the
|
||||
// installed version of Gradle. This dependency is documented at
|
||||
// http://tools.android.com/tech-docs/new-build-system/version-compatibility
|
||||
// and https://issues.apache.org/jira/browse/CB-8143
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.2.3'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
classpath 'com.android.tools.build:gradle:3.0.0'
|
||||
}
|
||||
}
|
||||
|
||||
// Allow plugins to declare Maven dependencies via build-extras.gradle.
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenCentral();
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '2.14.1'
|
||||
}
|
||||
|
||||
// Configuration properties. Set these via environment variables, build-extras.gradle, or gradle.properties.
|
||||
// Refer to: http://www.gradle.org/docs/current/userguide/tutorial_this_and_that.html
|
||||
ext {
|
||||
apply from: 'CordovaLib/cordova.gradle'
|
||||
// The value for android.compileSdkVersion.
|
||||
if (!project.hasProperty('cdvCompileSdkVersion')) {
|
||||
cdvCompileSdkVersion = null;
|
||||
}
|
||||
// The value for android.buildToolsVersion.
|
||||
if (!project.hasProperty('cdvBuildToolsVersion')) {
|
||||
cdvBuildToolsVersion = null;
|
||||
}
|
||||
// Sets the versionCode to the given value.
|
||||
if (!project.hasProperty('cdvVersionCode')) {
|
||||
cdvVersionCode = null
|
||||
}
|
||||
// Sets the minSdkVersion to the given value.
|
||||
if (!project.hasProperty('cdvMinSdkVersion')) {
|
||||
cdvMinSdkVersion = null
|
||||
}
|
||||
// Whether to build architecture-specific APKs.
|
||||
if (!project.hasProperty('cdvBuildMultipleApks')) {
|
||||
cdvBuildMultipleApks = null
|
||||
}
|
||||
// .properties files to use for release signing.
|
||||
if (!project.hasProperty('cdvReleaseSigningPropertiesFile')) {
|
||||
cdvReleaseSigningPropertiesFile = null
|
||||
}
|
||||
// .properties files to use for debug signing.
|
||||
if (!project.hasProperty('cdvDebugSigningPropertiesFile')) {
|
||||
cdvDebugSigningPropertiesFile = null
|
||||
}
|
||||
// Set by build.js script.
|
||||
if (!project.hasProperty('cdvBuildArch')) {
|
||||
cdvBuildArch = null
|
||||
}
|
||||
|
||||
// Plugin gradle extensions can append to this to have code run at the end.
|
||||
cdvPluginPostBuildExtras = []
|
||||
}
|
||||
|
||||
// PLUGIN GRADLE EXTENSIONS START
|
||||
apply from: "cordova-plugin-crosswalk-webview/dynamicbible-xwalk.gradle"
|
||||
// PLUGIN GRADLE EXTENSIONS END
|
||||
|
||||
def hasBuildExtras = file('build-extras.gradle').exists()
|
||||
if (hasBuildExtras) {
|
||||
apply from: 'build-extras.gradle'
|
||||
}
|
||||
|
||||
// Set property defaults after extension .gradle files.
|
||||
if (ext.cdvCompileSdkVersion == null) {
|
||||
ext.cdvCompileSdkVersion = privateHelpers.getProjectTarget()
|
||||
}
|
||||
if (ext.cdvBuildToolsVersion == null) {
|
||||
ext.cdvBuildToolsVersion = privateHelpers.findLatestInstalledBuildTools()
|
||||
}
|
||||
if (ext.cdvDebugSigningPropertiesFile == null && file('debug-signing.properties').exists()) {
|
||||
ext.cdvDebugSigningPropertiesFile = 'debug-signing.properties'
|
||||
}
|
||||
if (ext.cdvReleaseSigningPropertiesFile == null && file('release-signing.properties').exists()) {
|
||||
ext.cdvReleaseSigningPropertiesFile = 'release-signing.properties'
|
||||
}
|
||||
|
||||
// Cast to appropriate types.
|
||||
ext.cdvBuildMultipleApks = cdvBuildMultipleApks == null ? false : cdvBuildMultipleApks.toBoolean();
|
||||
ext.cdvMinSdkVersion = cdvMinSdkVersion == null ? null : Integer.parseInt('' + cdvMinSdkVersion)
|
||||
ext.cdvVersionCode = cdvVersionCode == null ? null : Integer.parseInt('' + cdvVersionCode)
|
||||
|
||||
def computeBuildTargetName(debugBuild) {
|
||||
def ret = 'assemble'
|
||||
if (cdvBuildMultipleApks && cdvBuildArch) {
|
||||
def arch = cdvBuildArch == 'arm' ? 'armv7' : cdvBuildArch
|
||||
ret += '' + arch.toUpperCase().charAt(0) + arch.substring(1);
|
||||
}
|
||||
return ret + (debugBuild ? 'Debug' : 'Release')
|
||||
}
|
||||
|
||||
// Make cdvBuild a task that depends on the debug/arch-sepecific task.
|
||||
task cdvBuildDebug
|
||||
cdvBuildDebug.dependsOn {
|
||||
return computeBuildTargetName(true)
|
||||
}
|
||||
|
||||
task cdvBuildRelease
|
||||
cdvBuildRelease.dependsOn {
|
||||
return computeBuildTargetName(false)
|
||||
}
|
||||
|
||||
task cdvPrintProps << {
|
||||
println('cdvCompileSdkVersion=' + cdvCompileSdkVersion)
|
||||
println('cdvBuildToolsVersion=' + cdvBuildToolsVersion)
|
||||
println('cdvVersionCode=' + cdvVersionCode)
|
||||
println('cdvMinSdkVersion=' + cdvMinSdkVersion)
|
||||
println('cdvBuildMultipleApks=' + cdvBuildMultipleApks)
|
||||
println('cdvReleaseSigningPropertiesFile=' + cdvReleaseSigningPropertiesFile)
|
||||
println('cdvDebugSigningPropertiesFile=' + cdvDebugSigningPropertiesFile)
|
||||
println('cdvBuildArch=' + cdvBuildArch)
|
||||
println('computedVersionCode=' + android.defaultConfig.versionCode)
|
||||
android.productFlavors.each { flavor ->
|
||||
println('computed' + flavor.name.capitalize() + 'VersionCode=' + flavor.versionCode)
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
sourceSets {
|
||||
main {
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
java.srcDirs = ['src']
|
||||
resources.srcDirs = ['src']
|
||||
aidl.srcDirs = ['src']
|
||||
renderscript.srcDirs = ['src']
|
||||
res.srcDirs = ['res']
|
||||
assets.srcDirs = ['assets']
|
||||
jniLibs.srcDirs = ['libs']
|
||||
maven {
|
||||
url "https://maven.google.com"
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
versionCode cdvVersionCode ?: new BigInteger("" + privateHelpers.extractIntFromManifest("versionCode"))
|
||||
applicationId privateHelpers.extractStringFromManifest("package")
|
||||
|
||||
if (cdvMinSdkVersion != null) {
|
||||
minSdkVersion cdvMinSdkVersion
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false;
|
||||
}
|
||||
|
||||
compileSdkVersion cdvCompileSdkVersion
|
||||
buildToolsVersion cdvBuildToolsVersion
|
||||
|
||||
if (Boolean.valueOf(cdvBuildMultipleApks)) {
|
||||
productFlavors {
|
||||
armv7 {
|
||||
versionCode defaultConfig.versionCode*10 + 2
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", ""
|
||||
}
|
||||
}
|
||||
x86 {
|
||||
versionCode defaultConfig.versionCode*10 + 4
|
||||
ndk {
|
||||
abiFilters "x86", ""
|
||||
}
|
||||
}
|
||||
all {
|
||||
ndk {
|
||||
abiFilters "all", ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
||||
ELSE NOTHING! DON'T MESS WITH THE VERSION CODE IF YOU DON'T HAVE TO!
|
||||
|
||||
else if (!cdvVersionCode) {
|
||||
def minSdkVersion = cdvMinSdkVersion ?: privateHelpers.extractIntFromManifest("minSdkVersion")
|
||||
// Vary versionCode by the two most common API levels:
|
||||
// 14 is ICS, which is the lowest API level for many apps.
|
||||
// 20 is Lollipop, which is the lowest API level for the updatable system webview.
|
||||
if (minSdkVersion >= 20) {
|
||||
defaultConfig.versionCode += 9
|
||||
} else if (minSdkVersion >= 14) {
|
||||
defaultConfig.versionCode += 8
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_6
|
||||
targetCompatibility JavaVersion.VERSION_1_6
|
||||
}
|
||||
|
||||
if (cdvReleaseSigningPropertiesFile) {
|
||||
signingConfigs {
|
||||
release {
|
||||
// These must be set or Gradle will complain (even if they are overridden).
|
||||
keyAlias = ""
|
||||
keyPassword = "__unset" // And these must be set to non-empty in order to have the signing step added to the task graph.
|
||||
storeFile = null
|
||||
storePassword = "__unset"
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
addSigningProps(cdvReleaseSigningPropertiesFile, signingConfigs.release)
|
||||
}
|
||||
if (cdvDebugSigningPropertiesFile) {
|
||||
addSigningProps(cdvDebugSigningPropertiesFile, signingConfigs.debug)
|
||||
//This replaces project.properties w.r.t. build settings
|
||||
project.ext {
|
||||
defaultBuildToolsVersion="25.0.2" //String
|
||||
defaultMinSdkVersion=19 //Integer - Minimum requirement is Android 4.4
|
||||
defaultTargetSdkVersion=26 //Integer - We ALWAYS target the latest by default
|
||||
defaultCompileSdkVersion=26 //Integer - We ALWAYS compile with the latest by default
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: '*.jar')
|
||||
// SUB-PROJECT DEPENDENCIES START
|
||||
debugCompile(project(path: "CordovaLib", configuration: "debug"))
|
||||
releaseCompile(project(path: "CordovaLib", configuration: "release"))
|
||||
// SUB-PROJECT DEPENDENCIES END
|
||||
}
|
||||
|
||||
def promptForReleaseKeyPassword() {
|
||||
if (!cdvReleaseSigningPropertiesFile) {
|
||||
return;
|
||||
}
|
||||
if ('__unset'.equals(android.signingConfigs.release.storePassword)) {
|
||||
android.signingConfigs.release.storePassword = privateHelpers.promptForPassword('Enter key store password: ')
|
||||
}
|
||||
if ('__unset'.equals(android.signingConfigs.release.keyPassword)) {
|
||||
android.signingConfigs.release.keyPassword = privateHelpers.promptForPassword('Enter key password: ');
|
||||
}
|
||||
}
|
||||
|
||||
gradle.taskGraph.whenReady { taskGraph ->
|
||||
taskGraph.getAllTasks().each() { task ->
|
||||
if (task.name == 'validateReleaseSigning' || task.name == 'validateSigningRelease') {
|
||||
promptForReleaseKeyPassword()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def addSigningProps(propsFilePath, signingConfig) {
|
||||
def propsFile = file(propsFilePath)
|
||||
def props = new Properties()
|
||||
propsFile.withReader { reader ->
|
||||
props.load(reader)
|
||||
}
|
||||
|
||||
def storeFile = new File(props.get('key.store') ?: privateHelpers.ensureValueExists(propsFilePath, props, 'storeFile'))
|
||||
if (!storeFile.isAbsolute()) {
|
||||
storeFile = RelativePath.parse(true, storeFile.toString()).getFile(propsFile.getParentFile())
|
||||
}
|
||||
if (!storeFile.exists()) {
|
||||
throw new FileNotFoundException('Keystore file does not exist: ' + storeFile.getAbsolutePath())
|
||||
}
|
||||
signingConfig.keyAlias = props.get('key.alias') ?: privateHelpers.ensureValueExists(propsFilePath, props, 'keyAlias')
|
||||
signingConfig.keyPassword = props.get('keyPassword', props.get('key.alias.password', signingConfig.keyPassword))
|
||||
signingConfig.storeFile = storeFile
|
||||
signingConfig.storePassword = props.get('storePassword', props.get('key.store.password', signingConfig.storePassword))
|
||||
def storeType = props.get('storeType', props.get('key.store.type', ''))
|
||||
if (!storeType) {
|
||||
def filename = storeFile.getName().toLowerCase();
|
||||
if (filename.endsWith('.p12') || filename.endsWith('.pfx')) {
|
||||
storeType = 'pkcs12'
|
||||
} else {
|
||||
storeType = signingConfig.storeType // "jks"
|
||||
}
|
||||
}
|
||||
signingConfig.storeType = storeType
|
||||
}
|
||||
|
||||
for (def func : cdvPluginPostBuildExtras) {
|
||||
func()
|
||||
}
|
||||
|
||||
// This can be defined within build-extras.gradle as:
|
||||
// ext.postBuildExtras = { ... code here ... }
|
||||
if (hasProperty('postBuildExtras')) {
|
||||
postBuildExtras()
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ def DEFAULT_MIN_SDK_VERSION = 14
|
||||
|
||||
def getConfigPreference(name) {
|
||||
name = name.toLowerCase()
|
||||
def xml = file("res/xml/config.xml").getText()
|
||||
def xml = file("src/main/res/xml/config.xml").getText()
|
||||
// Disable namespace awareness since Cordova doesn't use them properly
|
||||
def root = new XmlParser(false, false).parseText(xml)
|
||||
|
||||
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"node": true
|
||||
, "bitwise": true
|
||||
, "undef": true
|
||||
, "trailing": true
|
||||
, "quotmark": true
|
||||
, "indent": 4
|
||||
, "unused": "vars"
|
||||
, "latedef": "nofunc"
|
||||
}
|
150
DynamicBibleIonic/platforms/android/cordova/Api.js
vendored
@ -29,8 +29,7 @@ var selfEvents = require('cordova-common').events;
|
||||
|
||||
var PLATFORM = 'android';
|
||||
|
||||
|
||||
function setupEvents(externalEventEmitter) {
|
||||
function setupEvents (externalEventEmitter) {
|
||||
if (externalEventEmitter) {
|
||||
// This will make the platform internal events visible outside
|
||||
selfEvents.forwardEventsTo(externalEventEmitter);
|
||||
@ -43,7 +42,6 @@ function setupEvents(externalEventEmitter) {
|
||||
return selfEvents;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class, that acts as abstraction over particular platform. Encapsulates the
|
||||
* platform's properties and methods.
|
||||
@ -55,9 +53,10 @@ function setupEvents(externalEventEmitter) {
|
||||
*
|
||||
* * platform: String that defines a platform name.
|
||||
*/
|
||||
function Api(platform, platformRootDir, events) {
|
||||
function Api (platform, platformRootDir, events) {
|
||||
this.platform = PLATFORM;
|
||||
this.root = path.resolve(__dirname, '..');
|
||||
this.builder = 'gradle';
|
||||
|
||||
setupEvents(events);
|
||||
|
||||
@ -73,20 +72,24 @@ function Api(platform, platformRootDir, events) {
|
||||
strings: path.join(self.root, 'res/values/strings.xml'),
|
||||
manifest: path.join(self.root, 'AndroidManifest.xml'),
|
||||
build: path.join(self.root, 'build'),
|
||||
javaSrc: path.join(self.root, 'src'),
|
||||
// NOTE: Due to platformApi spec we need to return relative paths here
|
||||
cordovaJs: 'bin/templates/project/assets/www/cordova.js',
|
||||
cordovaJsSrc: 'cordova-js-src'
|
||||
};
|
||||
|
||||
// XXX Override some locations for Android Studio projects
|
||||
if(AndroidStudio.isAndroidStudioProject(self.root) === true) {
|
||||
selfEvents.emit('log', 'Android Studio project detected');
|
||||
this.android_studio = true;
|
||||
this.locations.configXml = path.join(self.root, 'app/src/main/res/xml/config.xml');
|
||||
this.locations.strings = path.join(self.root, 'app/src/main/res/xml/strings.xml');
|
||||
this.locations.manifest = path.join(self.root, 'app/src/main/AndroidManifest.xml');
|
||||
this.locations.www = path.join(self.root, 'app/src/main/assets/www');
|
||||
this.locations.res = path.join(self.root, 'app/src/main/res');
|
||||
if (AndroidStudio.isAndroidStudioProject(self.root) === true) {
|
||||
selfEvents.emit('log', 'Android Studio project detected');
|
||||
this.builder = 'studio';
|
||||
this.android_studio = true;
|
||||
this.locations.configXml = path.join(self.root, 'app/src/main/res/xml/config.xml');
|
||||
this.locations.strings = path.join(self.root, 'app/src/main/res/values/strings.xml');
|
||||
this.locations.manifest = path.join(self.root, 'app/src/main/AndroidManifest.xml');
|
||||
// We could have Java Source, we could have other languages
|
||||
this.locations.javaSrc = path.join(self.root, 'app/src/main/java/');
|
||||
this.locations.www = path.join(self.root, 'app/src/main/assets/www');
|
||||
this.locations.res = path.join(self.root, 'app/src/main/res');
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,16 +115,13 @@ Api.createPlatform = function (destination, config, options, events) {
|
||||
events = setupEvents(events);
|
||||
var result;
|
||||
try {
|
||||
result = require('../../lib/create')
|
||||
.create(destination, config, options, events)
|
||||
.then(function (destination) {
|
||||
result = require('../../lib/create').create(destination, config, options, events).then(function (destination) {
|
||||
var PlatformApi = require(path.resolve(destination, 'cordova/Api'));
|
||||
return new PlatformApi(PLATFORM, destination, events);
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
events.emit('error','createPlatform is not callable from the android project API.');
|
||||
throw(e);
|
||||
} catch (e) {
|
||||
events.emit('error', 'createPlatform is not callable from the android project API.');
|
||||
throw (e);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
@ -146,16 +146,13 @@ Api.updatePlatform = function (destination, options, events) {
|
||||
events = setupEvents(events);
|
||||
var result;
|
||||
try {
|
||||
result = require('../../lib/create')
|
||||
.update(destination, options, events)
|
||||
.then(function (destination) {
|
||||
result = require('../../lib/create').update(destination, options, events).then(function (destination) {
|
||||
var PlatformApi = require(path.resolve(destination, 'cordova/Api'));
|
||||
return new PlatformApi('android', destination, events);
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
events.emit('error','updatePlatform is not callable from the android project API, you will need to do this manually.');
|
||||
throw(e);
|
||||
} catch (e) {
|
||||
events.emit('error', 'updatePlatform is not callable from the android project API, you will need to do this manually.');
|
||||
throw (e);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
@ -226,40 +223,35 @@ Api.prototype.addPlugin = function (plugin, installOptions) {
|
||||
installOptions.variables.PACKAGE_NAME = project.getPackageName();
|
||||
}
|
||||
|
||||
if(this.android_studio === true) {
|
||||
installOptions.android_studio = true;
|
||||
if (this.android_studio === true) {
|
||||
installOptions.android_studio = true;
|
||||
}
|
||||
|
||||
return Q()
|
||||
.then(function () {
|
||||
//CB-11964: Do a clean when installing the plugin code to get around
|
||||
//the Gradle bug introduced by the Android Gradle Plugin Version 2.2
|
||||
//TODO: Delete when the next version of Android Gradle plugin comes out
|
||||
return Q().then(function () {
|
||||
// CB-11964: Do a clean when installing the plugin code to get around
|
||||
// the Gradle bug introduced by the Android Gradle Plugin Version 2.2
|
||||
// TODO: Delete when the next version of Android Gradle plugin comes out
|
||||
// Since clean doesn't just clean the build, it also wipes out www, we need
|
||||
// to pass additional options.
|
||||
|
||||
// Since clean doesn't just clean the build, it also wipes out www, we need
|
||||
// to pass additional options.
|
||||
// Do some basic argument parsing
|
||||
var opts = {};
|
||||
|
||||
// Do some basic argument parsing
|
||||
var opts = {};
|
||||
// Skip cleaning prepared files when not invoking via cordova CLI.
|
||||
opts.noPrepare = true;
|
||||
|
||||
// Skip cleaning prepared files when not invoking via cordova CLI.
|
||||
opts.noPrepare = true;
|
||||
|
||||
if(!AndroidStudio.isAndroidStudioProject(self.root) && !project.isClean()) {
|
||||
return self.clean(opts);
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
return PluginManager.get(self.platform, self.locations, project)
|
||||
.addPlugin(plugin, installOptions);
|
||||
})
|
||||
.then(function () {
|
||||
if (plugin.getFrameworks(this.platform).length === 0) return;
|
||||
|
||||
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
|
||||
require('./lib/builders/builders').getBuilder('gradle').prepBuildFiles();
|
||||
}.bind(this))
|
||||
// CB-11022 Return truthy value to prevent running prepare after
|
||||
if (!AndroidStudio.isAndroidStudioProject(self.root) && !project.isClean()) {
|
||||
return self.clean(opts);
|
||||
}
|
||||
}).then(function () {
|
||||
return PluginManager.get(self.platform, self.locations, project).addPlugin(plugin, installOptions);
|
||||
}).then(function () {
|
||||
if (plugin.getFrameworks(this.platform).length === 0) return;
|
||||
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
|
||||
// This should pick the correct builder, not just get gradle
|
||||
require('./lib/builders/builders').getBuilder(this.builder).prepBuildFiles();
|
||||
}.bind(this))
|
||||
// CB-11022 Return truthy value to prevent running prepare after
|
||||
.thenResolve(true);
|
||||
};
|
||||
|
||||
@ -279,9 +271,9 @@ Api.prototype.addPlugin = function (plugin, installOptions) {
|
||||
Api.prototype.removePlugin = function (plugin, uninstallOptions) {
|
||||
var project = AndroidProject.getProjectFile(this.root);
|
||||
|
||||
if(uninstallOptions && uninstallOptions.usePlatformWww === true && this.android_studio === true) {
|
||||
uninstallOptions.usePlatformWww = false;
|
||||
uninstallOptions.android_studio = true;
|
||||
if (uninstallOptions && uninstallOptions.usePlatformWww === true && this.android_studio === true) {
|
||||
uninstallOptions.usePlatformWww = false;
|
||||
uninstallOptions.android_studio = true;
|
||||
}
|
||||
|
||||
return PluginManager.get(this.platform, this.locations, project)
|
||||
@ -290,7 +282,7 @@ Api.prototype.removePlugin = function (plugin, uninstallOptions) {
|
||||
if (plugin.getFrameworks(this.platform).length === 0) return;
|
||||
|
||||
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
|
||||
require('./lib/builders/builders').getBuilder('gradle').prepBuildFiles();
|
||||
require('./lib/builders/builders').getBuilder(this.builder).prepBuildFiles();
|
||||
}.bind(this))
|
||||
// CB-11022 Return truthy value to prevent running prepare after
|
||||
.thenResolve(true);
|
||||
@ -343,11 +335,12 @@ Api.prototype.removePlugin = function (plugin, uninstallOptions) {
|
||||
*/
|
||||
Api.prototype.build = function (buildOptions) {
|
||||
var self = this;
|
||||
return require('./lib/check_reqs').run()
|
||||
.then(function () {
|
||||
if (this.android_studio) {
|
||||
buildOptions.studio = true;
|
||||
}
|
||||
return require('./lib/check_reqs').run().then(function () {
|
||||
return require('./lib/build').run.call(self, buildOptions);
|
||||
})
|
||||
.then(function (buildResults) {
|
||||
}).then(function (buildResults) {
|
||||
// Cast build result to array of build artifacts
|
||||
return buildResults.apkPaths.map(function (apkPath) {
|
||||
return {
|
||||
@ -372,10 +365,9 @@ Api.prototype.build = function (buildOptions) {
|
||||
* @return {Promise} A promise either fulfilled if package was built and ran
|
||||
* successfully, or rejected with CordovaError.
|
||||
*/
|
||||
Api.prototype.run = function(runOptions) {
|
||||
Api.prototype.run = function (runOptions) {
|
||||
var self = this;
|
||||
return require('./lib/check_reqs').run()
|
||||
.then(function () {
|
||||
return require('./lib/check_reqs').run().then(function () {
|
||||
return require('./lib/run').run.call(self, runOptions);
|
||||
});
|
||||
};
|
||||
@ -387,19 +379,23 @@ Api.prototype.run = function(runOptions) {
|
||||
* @return {Promise} Return a promise either fulfilled, or rejected with
|
||||
* CordovaError.
|
||||
*/
|
||||
Api.prototype.clean = function(cleanOptions) {
|
||||
Api.prototype.clean = function (cleanOptions) {
|
||||
var self = this;
|
||||
return require('./lib/check_reqs').run()
|
||||
.then(function () {
|
||||
return require('./lib/build').runClean.call(self, cleanOptions);
|
||||
})
|
||||
.then(function () {
|
||||
return require('./lib/prepare').clean.call(self, cleanOptions);
|
||||
});
|
||||
if (this.android_studio) {
|
||||
// This will lint, checking for null won't
|
||||
if (typeof cleanOptions === 'undefined') {
|
||||
cleanOptions = {};
|
||||
}
|
||||
cleanOptions.studio = true;
|
||||
}
|
||||
|
||||
return require('./lib/check_reqs').run().then(function () {
|
||||
return require('./lib/build').runClean.call(self, cleanOptions);
|
||||
}).then(function () {
|
||||
return require('./lib/prepare').clean.call(self, cleanOptions);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Performs a requirements check for current platform. Each platform defines its
|
||||
* own set of requirements, which should be resolved before platform can be
|
||||
@ -408,7 +404,7 @@ Api.prototype.clean = function(cleanOptions) {
|
||||
* @return {Promise<Requirement[]>} Promise, resolved with set of Requirement
|
||||
* objects for current platform.
|
||||
*/
|
||||
Api.prototype.requirements = function() {
|
||||
Api.prototype.requirements = function () {
|
||||
return require('./lib/check_reqs').check_all();
|
||||
};
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
@ECHO OFF
|
||||
SET script_path="%~dp0android_sdk_version"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
node %script_path% %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'android_sdk_version' script in 'bin' folder, aborting...>&2
|
||||
|
@ -18,7 +18,7 @@
|
||||
@ECHO OFF
|
||||
SET script_path="%~dp0check_reqs"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
node %script_path% %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'check_reqs' script in 'bin' folder, aborting...>&2
|
||||
|
@ -25,11 +25,11 @@ var CordovaError = require('cordova-common').CordovaError;
|
||||
|
||||
var Adb = {};
|
||||
|
||||
function isDevice(line) {
|
||||
function isDevice (line) {
|
||||
return line.match(/\w+\tdevice/) && !line.match(/emulator/);
|
||||
}
|
||||
|
||||
function isEmulator(line) {
|
||||
function isEmulator (line) {
|
||||
return line.match(/device/) && line.match(/emulator/);
|
||||
}
|
||||
|
||||
@ -44,8 +44,7 @@ function isEmulator(line) {
|
||||
* devices/emulators
|
||||
*/
|
||||
Adb.devices = function (opts) {
|
||||
return spawn('adb', ['devices'], {cwd: os.tmpdir()})
|
||||
.then(function(output) {
|
||||
return spawn('adb', ['devices'], {cwd: os.tmpdir()}).then(function (output) {
|
||||
return output.split('\n').filter(function (line) {
|
||||
// Filter out either real devices or emulators, depending on options
|
||||
return (line && opts && opts.emulators) ? isEmulator(line) : isDevice(line);
|
||||
@ -59,8 +58,7 @@ Adb.install = function (target, packagePath, opts) {
|
||||
events.emit('verbose', 'Installing apk ' + packagePath + ' on target ' + target + '...');
|
||||
var args = ['-s', target, 'install'];
|
||||
if (opts && opts.replace) args.push('-r');
|
||||
return spawn('adb', args.concat(packagePath), {cwd: os.tmpdir()})
|
||||
.then(function(output) {
|
||||
return spawn('adb', args.concat(packagePath), {cwd: os.tmpdir()}).then(function (output) {
|
||||
// 'adb install' seems to always returns no error, even if installation fails
|
||||
// so we catching output to detect installation failure
|
||||
if (output.match(/Failure/)) {
|
||||
@ -86,8 +84,7 @@ Adb.shell = function (target, shellCommand) {
|
||||
events.emit('verbose', 'Running adb shell command "' + shellCommand + '" on target ' + target + '...');
|
||||
var args = ['-s', target, 'shell'];
|
||||
shellCommand = shellCommand.split(/\s+/);
|
||||
return spawn('adb', args.concat(shellCommand), {cwd: os.tmpdir()})
|
||||
.catch(function (output) {
|
||||
return spawn('adb', args.concat(shellCommand), {cwd: os.tmpdir()}).catch(function (output) {
|
||||
return Q.reject(new CordovaError('Failed to execute shell command "' +
|
||||
shellCommand + '"" on device: ' + output));
|
||||
});
|
||||
@ -95,8 +92,7 @@ Adb.shell = function (target, shellCommand) {
|
||||
|
||||
Adb.start = function (target, activityName) {
|
||||
events.emit('verbose', 'Starting application "' + activityName + '" on target ' + target + '...');
|
||||
return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName)
|
||||
.catch(function (output) {
|
||||
return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName).catch(function (output) {
|
||||
return Q.reject(new CordovaError('Failed to start application "' +
|
||||
activityName + '"" on device: ' + output));
|
||||
});
|
||||
|
@ -19,12 +19,12 @@
|
||||
|
||||
var fs = require('fs');
|
||||
var et = require('elementtree');
|
||||
var xml= require('cordova-common').xmlHelpers;
|
||||
var xml = require('cordova-common').xmlHelpers;
|
||||
|
||||
var DEFAULT_ORIENTATION = 'default';
|
||||
|
||||
/** Wraps an AndroidManifest file */
|
||||
function AndroidManifest(path) {
|
||||
function AndroidManifest (path) {
|
||||
this.path = path;
|
||||
this.doc = xml.parseElementtreeSync(path);
|
||||
if (this.doc.getroot().tag !== 'manifest') {
|
||||
@ -32,38 +32,38 @@ function AndroidManifest(path) {
|
||||
}
|
||||
}
|
||||
|
||||
AndroidManifest.prototype.getVersionName = function() {
|
||||
AndroidManifest.prototype.getVersionName = function () {
|
||||
return this.doc.getroot().attrib['android:versionName'];
|
||||
};
|
||||
|
||||
AndroidManifest.prototype.setVersionName = function(versionName) {
|
||||
AndroidManifest.prototype.setVersionName = function (versionName) {
|
||||
this.doc.getroot().attrib['android:versionName'] = versionName;
|
||||
return this;
|
||||
};
|
||||
|
||||
AndroidManifest.prototype.getVersionCode = function() {
|
||||
AndroidManifest.prototype.getVersionCode = function () {
|
||||
return this.doc.getroot().attrib['android:versionCode'];
|
||||
};
|
||||
|
||||
AndroidManifest.prototype.setVersionCode = function(versionCode) {
|
||||
AndroidManifest.prototype.setVersionCode = function (versionCode) {
|
||||
this.doc.getroot().attrib['android:versionCode'] = versionCode;
|
||||
return this;
|
||||
};
|
||||
|
||||
AndroidManifest.prototype.getPackageId = function() {
|
||||
/*jshint -W069 */
|
||||
AndroidManifest.prototype.getPackageId = function () {
|
||||
/* jshint -W069 */
|
||||
return this.doc.getroot().attrib['package'];
|
||||
/*jshint +W069 */
|
||||
/* jshint +W069 */
|
||||
};
|
||||
|
||||
AndroidManifest.prototype.setPackageId = function(pkgId) {
|
||||
/*jshint -W069 */
|
||||
AndroidManifest.prototype.setPackageId = function (pkgId) {
|
||||
/* jshint -W069 */
|
||||
this.doc.getroot().attrib['package'] = pkgId;
|
||||
/*jshint +W069 */
|
||||
/* jshint +W069 */
|
||||
return this;
|
||||
};
|
||||
|
||||
AndroidManifest.prototype.getActivity = function() {
|
||||
AndroidManifest.prototype.getActivity = function () {
|
||||
var activity = this.doc.getroot().find('./application/activity');
|
||||
return {
|
||||
getName: function () {
|
||||
@ -102,17 +102,16 @@ AndroidManifest.prototype.getActivity = function() {
|
||||
};
|
||||
};
|
||||
|
||||
['minSdkVersion', 'maxSdkVersion', 'targetSdkVersion']
|
||||
.forEach(function(sdkPrefName) {
|
||||
['minSdkVersion', 'maxSdkVersion', 'targetSdkVersion'].forEach(function (sdkPrefName) {
|
||||
// Copy variable reference to avoid closure issues
|
||||
var prefName = sdkPrefName;
|
||||
|
||||
AndroidManifest.prototype['get' + capitalize(prefName)] = function() {
|
||||
AndroidManifest.prototype['get' + capitalize(prefName)] = function () {
|
||||
var usesSdk = this.doc.getroot().find('./uses-sdk');
|
||||
return usesSdk && usesSdk.attrib['android:' + prefName];
|
||||
};
|
||||
|
||||
AndroidManifest.prototype['set' + capitalize(prefName)] = function(prefValue) {
|
||||
AndroidManifest.prototype['set' + capitalize(prefName)] = function (prefValue) {
|
||||
var usesSdk = this.doc.getroot().find('./uses-sdk');
|
||||
|
||||
if (!usesSdk && prefValue) { // if there is no required uses-sdk element, we should create it first
|
||||
@ -128,11 +127,11 @@ AndroidManifest.prototype.getActivity = function() {
|
||||
};
|
||||
});
|
||||
|
||||
AndroidManifest.prototype.getDebuggable = function() {
|
||||
AndroidManifest.prototype.getDebuggable = function () {
|
||||
return this.doc.getroot().find('./application').attrib['android:debuggable'] === 'true';
|
||||
};
|
||||
|
||||
AndroidManifest.prototype.setDebuggable = function(value) {
|
||||
AndroidManifest.prototype.setDebuggable = function (value) {
|
||||
var application = this.doc.getroot().find('./application');
|
||||
if (value) {
|
||||
application.attrib['android:debuggable'] = 'true';
|
||||
@ -150,7 +149,7 @@ AndroidManifest.prototype.setDebuggable = function(value) {
|
||||
* @param {String} [destPath] File to write manifest to. If omitted,
|
||||
* manifest will be written to file it has been read from.
|
||||
*/
|
||||
AndroidManifest.prototype.write = function(destPath) {
|
||||
AndroidManifest.prototype.write = function (destPath) {
|
||||
fs.writeFileSync(destPath || this.path, this.doc.write({indent: 4}), 'utf-8');
|
||||
};
|
||||
|
||||
|
@ -26,16 +26,15 @@ var pluginHandlers = require('./pluginHandlers');
|
||||
|
||||
var projectFileCache = {};
|
||||
|
||||
function addToPropertyList(projectProperties, key, value) {
|
||||
function addToPropertyList (projectProperties, key, value) {
|
||||
var i = 1;
|
||||
while (projectProperties.get(key + '.' + i))
|
||||
i++;
|
||||
while (projectProperties.get(key + '.' + i)) { i++; }
|
||||
|
||||
projectProperties.set(key + '.' + i, value);
|
||||
projectProperties.dirty = true;
|
||||
}
|
||||
|
||||
function removeFromPropertyList(projectProperties, key, value) {
|
||||
function removeFromPropertyList (projectProperties, key, value) {
|
||||
var i = 1;
|
||||
var currentValue;
|
||||
while ((currentValue = projectProperties.get(key + '.' + i))) {
|
||||
@ -54,18 +53,18 @@ function removeFromPropertyList(projectProperties, key, value) {
|
||||
|
||||
function getRelativeLibraryPath (parentDir, subDir) {
|
||||
var libraryPath = path.relative(parentDir, subDir);
|
||||
return (path.sep == '\\') ? libraryPath.replace(/\\/g, '/') : libraryPath;
|
||||
return (path.sep === '\\') ? libraryPath.replace(/\\/g, '/') : libraryPath;
|
||||
}
|
||||
|
||||
function AndroidProject(projectDir) {
|
||||
function AndroidProject (projectDir) {
|
||||
this._propertiesEditors = {};
|
||||
this._subProjectDirs = {};
|
||||
this._dirty = false;
|
||||
this.projectDir = projectDir;
|
||||
this.platformWww = path.join(this.projectDir, 'platform_www');
|
||||
this.www = path.join(this.projectDir, 'assets/www');
|
||||
if(AndroidStudio.isAndroidStudioProject(projectDir) === true) {
|
||||
this.www = path.join(this.projectDir, 'app/src/main/assets/www');
|
||||
if (AndroidStudio.isAndroidStudioProject(projectDir) === true) {
|
||||
this.www = path.join(this.projectDir, 'app/src/main/assets/www');
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,15 +91,15 @@ AndroidProject.purgeCache = function (projectDir) {
|
||||
*
|
||||
* @return {String} The name of the package
|
||||
*/
|
||||
AndroidProject.prototype.getPackageName = function() {
|
||||
AndroidProject.prototype.getPackageName = function () {
|
||||
var manifestPath = path.join(this.projectDir, 'AndroidManifest.xml');
|
||||
if(AndroidStudio.isAndroidStudioProject(this.projectDir) === true) {
|
||||
manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml');
|
||||
if (AndroidStudio.isAndroidStudioProject(this.projectDir) === true) {
|
||||
manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml');
|
||||
}
|
||||
return new AndroidManifest(manifestPath).getPackageId();
|
||||
};
|
||||
|
||||
AndroidProject.prototype.getCustomSubprojectRelativeDir = function(plugin_id, src) {
|
||||
AndroidProject.prototype.getCustomSubprojectRelativeDir = function (plugin_id, src) {
|
||||
// All custom subprojects are prefixed with the last portion of the package id.
|
||||
// This is to avoid collisions when opening multiple projects in Eclipse that have subprojects with the same name.
|
||||
var packageName = this.getPackageName();
|
||||
@ -110,7 +109,7 @@ AndroidProject.prototype.getCustomSubprojectRelativeDir = function(plugin_id, sr
|
||||
return subRelativeDir;
|
||||
};
|
||||
|
||||
AndroidProject.prototype.addSubProject = function(parentDir, subDir) {
|
||||
AndroidProject.prototype.addSubProject = function (parentDir, subDir) {
|
||||
var parentProjectFile = path.resolve(parentDir, 'project.properties');
|
||||
var subProjectFile = path.resolve(subDir, 'project.properties');
|
||||
var parentProperties = this._getPropertiesFile(parentProjectFile);
|
||||
@ -126,7 +125,7 @@ AndroidProject.prototype.addSubProject = function(parentDir, subDir) {
|
||||
this._dirty = true;
|
||||
};
|
||||
|
||||
AndroidProject.prototype.removeSubProject = function(parentDir, subDir) {
|
||||
AndroidProject.prototype.removeSubProject = function (parentDir, subDir) {
|
||||
var parentProjectFile = path.resolve(parentDir, 'project.properties');
|
||||
var parentProperties = this._getPropertiesFile(parentProjectFile);
|
||||
removeFromPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir));
|
||||
@ -134,35 +133,35 @@ AndroidProject.prototype.removeSubProject = function(parentDir, subDir) {
|
||||
this._dirty = true;
|
||||
};
|
||||
|
||||
AndroidProject.prototype.addGradleReference = function(parentDir, subDir) {
|
||||
AndroidProject.prototype.addGradleReference = function (parentDir, subDir) {
|
||||
var parentProjectFile = path.resolve(parentDir, 'project.properties');
|
||||
var parentProperties = this._getPropertiesFile(parentProjectFile);
|
||||
addToPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
|
||||
this._dirty = true;
|
||||
};
|
||||
|
||||
AndroidProject.prototype.removeGradleReference = function(parentDir, subDir) {
|
||||
AndroidProject.prototype.removeGradleReference = function (parentDir, subDir) {
|
||||
var parentProjectFile = path.resolve(parentDir, 'project.properties');
|
||||
var parentProperties = this._getPropertiesFile(parentProjectFile);
|
||||
removeFromPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
|
||||
this._dirty = true;
|
||||
};
|
||||
|
||||
AndroidProject.prototype.addSystemLibrary = function(parentDir, value) {
|
||||
AndroidProject.prototype.addSystemLibrary = function (parentDir, value) {
|
||||
var parentProjectFile = path.resolve(parentDir, 'project.properties');
|
||||
var parentProperties = this._getPropertiesFile(parentProjectFile);
|
||||
addToPropertyList(parentProperties, 'cordova.system.library', value);
|
||||
this._dirty = true;
|
||||
};
|
||||
|
||||
AndroidProject.prototype.removeSystemLibrary = function(parentDir, value) {
|
||||
AndroidProject.prototype.removeSystemLibrary = function (parentDir, value) {
|
||||
var parentProjectFile = path.resolve(parentDir, 'project.properties');
|
||||
var parentProperties = this._getPropertiesFile(parentProjectFile);
|
||||
removeFromPropertyList(parentProperties, 'cordova.system.library', value);
|
||||
this._dirty = true;
|
||||
};
|
||||
|
||||
AndroidProject.prototype.write = function() {
|
||||
AndroidProject.prototype.write = function () {
|
||||
if (!this._dirty) {
|
||||
return;
|
||||
}
|
||||
@ -201,9 +200,9 @@ AndroidProject.prototype.getUninstaller = function (type) {
|
||||
* This checks if an Android project is clean or has old build artifacts
|
||||
*/
|
||||
|
||||
AndroidProject.prototype.isClean = function() {
|
||||
AndroidProject.prototype.isClean = function () {
|
||||
var build_path = path.join(this.projectDir, 'build');
|
||||
//If the build directory doesn't exist, it's clean
|
||||
// If the build directory doesn't exist, it's clean
|
||||
return !(fs.existsSync(build_path));
|
||||
};
|
||||
|
||||
|
@ -4,35 +4,35 @@
|
||||
* @param {String} root Root folder of the project
|
||||
*/
|
||||
|
||||
/*jshint esnext: false */
|
||||
/* jshint esnext: false */
|
||||
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
|
||||
module.exports.isAndroidStudioProject = function isAndroidStudioProject(root) {
|
||||
var eclipseFiles = ['AndroidManifest.xml', 'libs', 'res', 'project.properties', 'platform_www'];
|
||||
var androidStudioFiles = ['app', 'gradle', 'app/src/main/res'];
|
||||
module.exports.isAndroidStudioProject = function isAndroidStudioProject (root) {
|
||||
var eclipseFiles = ['AndroidManifest.xml', 'libs', 'res'];
|
||||
var androidStudioFiles = ['app', 'app/src/main'];
|
||||
|
||||
// assume it is an AS project and not an Eclipse project
|
||||
var isEclipse = false;
|
||||
var isAS = true;
|
||||
|
||||
if(!fs.existsSync(root)) {
|
||||
if (!fs.existsSync(root)) {
|
||||
throw new CordovaError('AndroidStudio.js:inAndroidStudioProject root does not exist: ' + root);
|
||||
}
|
||||
|
||||
// if any of the following exists, then we are not an ASProj
|
||||
eclipseFiles.forEach(function(file) {
|
||||
if(fs.existsSync(path.join(root, file))) {
|
||||
eclipseFiles.forEach(function (file) {
|
||||
if (fs.existsSync(path.join(root, file))) {
|
||||
isEclipse = true;
|
||||
}
|
||||
});
|
||||
|
||||
// if it is NOT an eclipse project, check that all required files exist
|
||||
if(!isEclipse) {
|
||||
androidStudioFiles.forEach(function(file){
|
||||
if(!fs.existsSync(path.join(root, file))) {
|
||||
if (!isEclipse) {
|
||||
androidStudioFiles.forEach(function (file) {
|
||||
if (!fs.existsSync(path.join(root, file))) {
|
||||
console.log('missing file :: ' + file);
|
||||
isAS = false;
|
||||
}
|
||||
|
@ -17,8 +17,8 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var Q = require('q'),
|
||||
superspawn = require('cordova-common').superspawn;
|
||||
var Q = require('q');
|
||||
var superspawn = require('cordova-common').superspawn;
|
||||
|
||||
var suffix_number_regex = /(\d+)$/;
|
||||
// Used for sorting Android targets, example strings to sort:
|
||||
@ -29,7 +29,7 @@ var suffix_number_regex = /(\d+)$/;
|
||||
// The idea is to sort based on largest "suffix" number - meaning the bigger
|
||||
// the number at the end, the more recent the target, the closer to the
|
||||
// start of the array.
|
||||
function sort_by_largest_numerical_suffix(a, b) {
|
||||
function sort_by_largest_numerical_suffix (a, b) {
|
||||
var suffix_a = a.match(suffix_number_regex);
|
||||
var suffix_b = b.match(suffix_number_regex);
|
||||
if (suffix_a && suffix_b) {
|
||||
@ -43,9 +43,8 @@ function sort_by_largest_numerical_suffix(a, b) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.print_newest_available_sdk_target = function() {
|
||||
return module.exports.list_targets()
|
||||
.then(function(targets) {
|
||||
module.exports.print_newest_available_sdk_target = function () {
|
||||
return module.exports.list_targets().then(function (targets) {
|
||||
targets.sort(sort_by_largest_numerical_suffix);
|
||||
console.log(targets[0]);
|
||||
});
|
||||
@ -66,38 +65,34 @@ module.exports.version_string_to_api_level = {
|
||||
'7.1.1': 25
|
||||
};
|
||||
|
||||
function parse_targets(output) {
|
||||
function parse_targets (output) {
|
||||
var target_out = output.split('\n');
|
||||
var targets = [];
|
||||
for (var i = target_out.length - 1; i >= 0; i--) {
|
||||
if(target_out[i].match(/id:/)) { // if "id:" is in the line...
|
||||
targets.push(target_out[i].match(/"(.+)"/)[1]); //.. match whatever is in quotes.
|
||||
if (target_out[i].match(/id:/)) { // if "id:" is in the line...
|
||||
targets.push(target_out[i].match(/"(.+)"/)[1]); // .. match whatever is in quotes.
|
||||
}
|
||||
}
|
||||
return targets;
|
||||
}
|
||||
|
||||
module.exports.list_targets_with_android = function() {
|
||||
return superspawn.spawn('android', ['list', 'target'])
|
||||
.then(parse_targets);
|
||||
module.exports.list_targets_with_android = function () {
|
||||
return superspawn.spawn('android', ['list', 'target']).then(parse_targets);
|
||||
};
|
||||
|
||||
module.exports.list_targets_with_avdmanager = function() {
|
||||
return superspawn.spawn('avdmanager', ['list', 'target'])
|
||||
.then(parse_targets);
|
||||
module.exports.list_targets_with_avdmanager = function () {
|
||||
return superspawn.spawn('avdmanager', ['list', 'target']).then(parse_targets);
|
||||
};
|
||||
|
||||
module.exports.list_targets = function() {
|
||||
return module.exports.list_targets_with_avdmanager()
|
||||
.catch(function(err) {
|
||||
module.exports.list_targets = function () {
|
||||
return module.exports.list_targets_with_avdmanager().catch(function (err) {
|
||||
// If there's an error, like avdmanager could not be found, we can try
|
||||
// as a last resort, to run `android`, in case this is a super old
|
||||
// SDK installation.
|
||||
if (err && (err.code == 'ENOENT' || (err.stderr && err.stderr.match(/not recognized/)))) {
|
||||
if (err && (err.code === 'ENOENT' || (err.stderr && err.stderr.match(/not recognized/)))) {
|
||||
return module.exports.list_targets_with_android();
|
||||
} else throw err;
|
||||
})
|
||||
.then(function(targets) {
|
||||
}).then(function (targets) {
|
||||
if (targets.length === 0) {
|
||||
return Q.reject(new Error('No android targets (SDKs) installed!'));
|
||||
}
|
||||
|
@ -19,10 +19,10 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var Q = require('q'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
nopt = require('nopt');
|
||||
var Q = require('q');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var nopt = require('nopt');
|
||||
|
||||
var Adb = require('./Adb');
|
||||
|
||||
@ -31,11 +31,11 @@ var events = require('cordova-common').events;
|
||||
var spawn = require('cordova-common').superspawn.spawn;
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
|
||||
function parseOpts(options, resolvedTarget, projectRoot) {
|
||||
function parseOpts (options, resolvedTarget, projectRoot) {
|
||||
options = options || {};
|
||||
options.argv = nopt({
|
||||
gradle: Boolean,
|
||||
ant: Boolean,
|
||||
studio: Boolean,
|
||||
prepenv: Boolean,
|
||||
versionCode: String,
|
||||
minSdkVersion: String,
|
||||
@ -47,24 +47,28 @@ function parseOpts(options, resolvedTarget, projectRoot) {
|
||||
keystoreType: String
|
||||
}, {}, options.argv, 0);
|
||||
|
||||
// Android Studio Build method is the default
|
||||
var ret = {
|
||||
buildType: options.release ? 'release' : 'debug',
|
||||
buildMethod: process.env.ANDROID_BUILD || 'gradle',
|
||||
buildMethod: process.env.ANDROID_BUILD || 'studio',
|
||||
prepEnv: options.argv.prepenv,
|
||||
arch: resolvedTarget && resolvedTarget.arch,
|
||||
extraArgs: []
|
||||
};
|
||||
|
||||
if (options.argv.ant || options.argv.gradle)
|
||||
ret.buildMethod = options.argv.ant ? 'ant' : 'gradle';
|
||||
if (options.argv.gradle || options.argv.studio) {
|
||||
ret.buildMethod = options.argv.studio ? 'studio' : 'gradle';
|
||||
}
|
||||
|
||||
// This comes from cordova/run
|
||||
if (options.studio) ret.buildMethod = 'studio';
|
||||
if (options.gradle) ret.buildMethod = 'gradle';
|
||||
|
||||
if (options.nobuild) ret.buildMethod = 'none';
|
||||
|
||||
if (options.argv.versionCode)
|
||||
ret.extraArgs.push('-PcdvVersionCode=' + options.argv.versionCode);
|
||||
if (options.argv.versionCode) { ret.extraArgs.push('-PcdvVersionCode=' + options.argv.versionCode); }
|
||||
|
||||
if (options.argv.minSdkVersion)
|
||||
ret.extraArgs.push('-PcdvMinSdkVersion=' + options.argv.minSdkVersion);
|
||||
if (options.argv.minSdkVersion) { ret.extraArgs.push('-PcdvMinSdkVersion=' + options.argv.minSdkVersion); }
|
||||
|
||||
if (options.argv.gradleArg) {
|
||||
ret.extraArgs = ret.extraArgs.concat(options.argv.gradleArg);
|
||||
@ -72,12 +76,10 @@ function parseOpts(options, resolvedTarget, projectRoot) {
|
||||
|
||||
var packageArgs = {};
|
||||
|
||||
if (options.argv.keystore)
|
||||
packageArgs.keystore = path.relative(projectRoot, path.resolve(options.argv.keystore));
|
||||
if (options.argv.keystore) { packageArgs.keystore = path.relative(projectRoot, path.resolve(options.argv.keystore)); }
|
||||
|
||||
['alias','storePassword','password','keystoreType'].forEach(function (flagName) {
|
||||
if (options.argv[flagName])
|
||||
packageArgs[flagName] = options.argv[flagName];
|
||||
['alias', 'storePassword', 'password', 'keystoreType'].forEach(function (flagName) {
|
||||
if (options.argv[flagName]) { packageArgs[flagName] = options.argv[flagName]; }
|
||||
});
|
||||
|
||||
var buildConfig = options.buildConfig;
|
||||
@ -88,20 +90,20 @@ function parseOpts(options, resolvedTarget, projectRoot) {
|
||||
if (!fs.existsSync(buildConfig)) {
|
||||
throw new Error('Specified build config file does not exist: ' + buildConfig);
|
||||
}
|
||||
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 config = JSON.parse(buildjson.replace(/^\ufeff/, '')); // Remove BOM
|
||||
if (config.android && config.android[ret.buildType]) {
|
||||
var androidInfo = config.android[ret.buildType];
|
||||
if(androidInfo.keystore && !packageArgs.keystore) {
|
||||
if(androidInfo.keystore.substr(0,1) === '~') {
|
||||
if (androidInfo.keystore && !packageArgs.keystore) {
|
||||
if (androidInfo.keystore.substr(0, 1) === '~') {
|
||||
androidInfo.keystore = process.env.HOME + androidInfo.keystore.substr(1);
|
||||
}
|
||||
packageArgs.keystore = path.resolve(path.dirname(buildConfig), androidInfo.keystore);
|
||||
events.emit('log', 'Reading the keystore from: ' + packageArgs.keystore);
|
||||
}
|
||||
|
||||
['alias', 'storePassword', 'password','keystoreType'].forEach(function (key){
|
||||
['alias', 'storePassword', 'password', 'keystoreType'].forEach(function (key) {
|
||||
packageArgs[key] = packageArgs[key] || androidInfo[key];
|
||||
});
|
||||
}
|
||||
@ -112,8 +114,8 @@ function parseOpts(options, resolvedTarget, projectRoot) {
|
||||
packageArgs.password, packageArgs.keystoreType);
|
||||
}
|
||||
|
||||
if(!ret.packageInfo) {
|
||||
if(Object.keys(packageArgs).length > 0) {
|
||||
if (!ret.packageInfo) {
|
||||
if (Object.keys(packageArgs).length > 0) {
|
||||
events.emit('warn', '\'keystore\' and \'alias\' need to be specified to generate a signed archive.');
|
||||
}
|
||||
}
|
||||
@ -125,11 +127,10 @@ function parseOpts(options, resolvedTarget, projectRoot) {
|
||||
* Builds the project with the specifed options
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.runClean = function(options) {
|
||||
module.exports.runClean = function (options) {
|
||||
var opts = parseOpts(options, null, this.root);
|
||||
var builder = builders.getBuilder(opts.buildMethod);
|
||||
return builder.prepEnv(opts)
|
||||
.then(function() {
|
||||
return builder.prepEnv(opts).then(function () {
|
||||
return builder.clean(opts);
|
||||
});
|
||||
};
|
||||
@ -146,17 +147,16 @@ module.exports.runClean = function(options) {
|
||||
* @return {Promise<Object>} Promise, resolved with built packages
|
||||
* information.
|
||||
*/
|
||||
module.exports.run = function(options, optResolvedTarget) {
|
||||
module.exports.run = function (options, optResolvedTarget) {
|
||||
var opts = parseOpts(options, optResolvedTarget, this.root);
|
||||
console.log(opts.buildMethod);
|
||||
var builder = builders.getBuilder(opts.buildMethod);
|
||||
return builder.prepEnv(opts)
|
||||
.then(function() {
|
||||
return builder.prepEnv(opts).then(function () {
|
||||
if (opts.prepEnv) {
|
||||
events.emit('verbose', 'Build file successfully prepared.');
|
||||
return;
|
||||
}
|
||||
return builder.build(opts)
|
||||
.then(function() {
|
||||
return builder.build(opts).then(function () {
|
||||
var apkPaths = builder.findOutputApks(opts.buildType, opts.arch);
|
||||
events.emit('log', 'Built the following apk(s): \n\t' + apkPaths.join('\n\t'));
|
||||
return {
|
||||
@ -172,38 +172,31 @@ module.exports.run = function(options, optResolvedTarget) {
|
||||
* Detects the architecture of a device/emulator
|
||||
* Returns "arm" or "x86".
|
||||
*/
|
||||
module.exports.detectArchitecture = function(target) {
|
||||
function helper() {
|
||||
return Adb.shell(target, 'cat /proc/cpuinfo')
|
||||
.then(function(output) {
|
||||
module.exports.detectArchitecture = function (target) {
|
||||
function helper () {
|
||||
return Adb.shell(target, 'cat /proc/cpuinfo').then(function (output) {
|
||||
return /intel/i.exec(output) ? 'x86' : 'arm';
|
||||
});
|
||||
}
|
||||
// It sometimes happens (at least on OS X), that this command will hang forever.
|
||||
// To fix it, either unplug & replug device, or restart adb server.
|
||||
return helper()
|
||||
.timeout(1000, new CordovaError('Device communication timed out. Try unplugging & replugging the device.'))
|
||||
.then(null, function(err) {
|
||||
return helper().timeout(1000, new CordovaError('Device communication timed out. Try unplugging & replugging the device.')).then(null, function (err) {
|
||||
if (/timed out/.exec('' + err)) {
|
||||
// adb kill-server doesn't seem to do the trick.
|
||||
// Could probably find a x-platform version of killall, but I'm not actually
|
||||
// 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'])
|
||||
.then(function() {
|
||||
return helper()
|
||||
.then(null, function() {
|
||||
return spawn('killall', ['adb']).then(function () {
|
||||
return helper().then(null, function () {
|
||||
// The double kill is sadly often necessary, at least on mac.
|
||||
events.emit('warn', 'adb timed out a second time while detecting device/emulator architecture. Killing adb and trying again.');
|
||||
return spawn('killall', ['adb'])
|
||||
.then(function() {
|
||||
return helper()
|
||||
.then(null, function() {
|
||||
return spawn('killall', ['adb']).then(function () {
|
||||
return helper().then(null, function () {
|
||||
return Q.reject(new CordovaError('adb timed out a third time while detecting device/emulator architecture. Try unplugging & replugging the device.'));
|
||||
});
|
||||
});
|
||||
});
|
||||
}, function() {
|
||||
}, function () {
|
||||
// For non-killall OS's.
|
||||
return Q.reject(err);
|
||||
});
|
||||
@ -212,10 +205,10 @@ module.exports.detectArchitecture = function(target) {
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.findBestApkForArchitecture = function(buildResults, arch) {
|
||||
var paths = buildResults.apkPaths.filter(function(p) {
|
||||
module.exports.findBestApkForArchitecture = function (buildResults, arch) {
|
||||
var paths = buildResults.apkPaths.filter(function (p) {
|
||||
var apkName = path.basename(p);
|
||||
if (buildResults.buildType == 'debug') {
|
||||
if (buildResults.buildType === 'debug') {
|
||||
return /-debug/.exec(apkName);
|
||||
}
|
||||
return !/-debug/.exec(apkName);
|
||||
@ -235,7 +228,7 @@ module.exports.findBestApkForArchitecture = function(buildResults, arch) {
|
||||
throw new Error('Could not find apk architecture: ' + arch + ' build-type: ' + buildResults.buildType);
|
||||
};
|
||||
|
||||
function PackageInfo(keystore, alias, storePassword, password, keystoreType) {
|
||||
function PackageInfo (keystore, alias, storePassword, password, keystoreType) {
|
||||
this.keystore = {
|
||||
'name': 'key.store',
|
||||
'value': keystore
|
||||
@ -265,10 +258,10 @@ function PackageInfo(keystore, alias, storePassword, password, keystoreType) {
|
||||
}
|
||||
|
||||
PackageInfo.prototype = {
|
||||
toProperties: function() {
|
||||
toProperties: function () {
|
||||
var self = this;
|
||||
var result = '';
|
||||
Object.keys(self).forEach(function(key) {
|
||||
Object.keys(self).forEach(function (key) {
|
||||
result += self[key].name;
|
||||
result += '=';
|
||||
result += self[key].value.replace(/\\/g, '\\\\');
|
||||
@ -278,7 +271,7 @@ PackageInfo.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.help = function() {
|
||||
module.exports.help = function () {
|
||||
console.log('Usage: ' + path.relative(process.cwd(), path.join('../build')) + ' [flags] [Signed APK flags]');
|
||||
console.log('Flags:');
|
||||
console.log(' \'--debug\': will build project in debug mode (default)');
|
||||
|
@ -1,156 +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 Q = require('q');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var util = require('util');
|
||||
var shell = require('shelljs');
|
||||
var spawn = require('cordova-common').superspawn.spawn;
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
var check_reqs = require('../check_reqs');
|
||||
|
||||
var SIGNING_PROPERTIES = '-signing.properties';
|
||||
var MARKER = 'YOUR CHANGES WILL BE ERASED!';
|
||||
var TEMPLATE =
|
||||
'# This file is automatically generated.\n' +
|
||||
'# Do not modify this file -- ' + MARKER + '\n';
|
||||
|
||||
var GenericBuilder = require('./GenericBuilder');
|
||||
|
||||
function AntBuilder (projectRoot) {
|
||||
GenericBuilder.call(this, projectRoot);
|
||||
|
||||
this.binDirs = {ant: this.binDirs.ant};
|
||||
}
|
||||
|
||||
util.inherits(AntBuilder, GenericBuilder);
|
||||
|
||||
AntBuilder.prototype.getArgs = function(cmd, opts) {
|
||||
var args = [cmd, '-f', path.join(this.root, 'build.xml')];
|
||||
// custom_rules.xml is required for incremental builds.
|
||||
if (hasCustomRules(this.root)) {
|
||||
args.push('-Dout.dir=ant-build', '-Dgen.absolute.dir=ant-gen');
|
||||
}
|
||||
if(opts.packageInfo) {
|
||||
args.push('-propertyfile=' + path.join(this.root, opts.buildType + SIGNING_PROPERTIES));
|
||||
}
|
||||
return args;
|
||||
};
|
||||
|
||||
AntBuilder.prototype.prepEnv = function(opts) {
|
||||
var self = this;
|
||||
return check_reqs.check_ant()
|
||||
.then(function() {
|
||||
// Copy in build.xml on each build so that:
|
||||
// A) we don't require the Android SDK at project creation time, and
|
||||
// B) we always use the SDK's latest version of it.
|
||||
/*jshint -W069 */
|
||||
var sdkDir = process.env['ANDROID_HOME'];
|
||||
/*jshint +W069 */
|
||||
var buildTemplate = fs.readFileSync(path.join(sdkDir, 'tools', 'lib', 'build.template'), 'utf8');
|
||||
function writeBuildXml(projectPath) {
|
||||
var newData = buildTemplate.replace('PROJECT_NAME', self.extractRealProjectNameFromManifest());
|
||||
fs.writeFileSync(path.join(projectPath, 'build.xml'), newData);
|
||||
if (!fs.existsSync(path.join(projectPath, 'local.properties'))) {
|
||||
fs.writeFileSync(path.join(projectPath, 'local.properties'), TEMPLATE);
|
||||
}
|
||||
}
|
||||
writeBuildXml(self.root);
|
||||
var propertiesObj = self.readProjectProperties();
|
||||
var subProjects = propertiesObj.libs;
|
||||
for (var i = 0; i < subProjects.length; ++i) {
|
||||
writeBuildXml(path.join(self.root, subProjects[i]));
|
||||
}
|
||||
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. Use gradle instead.');
|
||||
}
|
||||
|
||||
var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
|
||||
var propertiesFilePath = path.join(self.root, propertiesFile);
|
||||
if (opts.packageInfo) {
|
||||
fs.writeFileSync(propertiesFilePath, TEMPLATE + opts.packageInfo.toProperties());
|
||||
} else if(isAutoGenerated(propertiesFilePath)) {
|
||||
shell.rm('-f', propertiesFilePath);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* Builds the project with ant.
|
||||
* Returns a promise.
|
||||
*/
|
||||
AntBuilder.prototype.build = function(opts) {
|
||||
// Without our custom_rules.xml, we need to clean before building.
|
||||
var ret = Q();
|
||||
if (!hasCustomRules(this.root)) {
|
||||
// clean will call check_ant() for us.
|
||||
ret = this.clean(opts);
|
||||
}
|
||||
|
||||
var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts);
|
||||
return check_reqs.check_ant()
|
||||
.then(function() {
|
||||
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);
|
||||
});
|
||||
};
|
||||
|
||||
AntBuilder.prototype.clean = function(opts) {
|
||||
var args = this.getArgs('clean', opts);
|
||||
var self = this;
|
||||
return check_reqs.check_ant()
|
||||
.then(function() {
|
||||
return spawn('ant', args, {stdio: 'inherit'});
|
||||
})
|
||||
.then(function () {
|
||||
shell.rm('-rf', path.join(self.root, 'out'));
|
||||
|
||||
['debug', 'release'].forEach(function(config) {
|
||||
var propertiesFilePath = path.join(self.root, config + SIGNING_PROPERTIES);
|
||||
if(isAutoGenerated(propertiesFilePath)){
|
||||
shell.rm('-f', propertiesFilePath);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = AntBuilder;
|
||||
|
||||
function hasCustomRules(projectRoot) {
|
||||
return fs.existsSync(path.join(projectRoot, 'custom_rules.xml'));
|
||||
}
|
||||
|
||||
function isAutoGenerated(file) {
|
||||
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
|
||||
}
|
@ -16,83 +16,55 @@
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
/* eslint no-self-assign: 0 */
|
||||
/* eslint no-unused-vars: 0 */
|
||||
|
||||
var Q = require('q');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var shell = require('shelljs');
|
||||
var events = require('cordova-common').events;
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
|
||||
function GenericBuilder (projectDir) {
|
||||
this.root = projectDir || path.resolve(__dirname, '../../..');
|
||||
this.binDirs = {
|
||||
ant: path.join(this.root, hasCustomRules(this.root) ? 'ant-build' : 'bin'),
|
||||
studio: path.join(this.root, 'app', 'build', 'outputs', 'apk'),
|
||||
gradle: path.join(this.root, 'build', 'outputs', 'apk')
|
||||
};
|
||||
}
|
||||
|
||||
function hasCustomRules(projectRoot) {
|
||||
return fs.existsSync(path.join(projectRoot, 'custom_rules.xml'));
|
||||
}
|
||||
|
||||
GenericBuilder.prototype.prepEnv = function() {
|
||||
GenericBuilder.prototype.prepEnv = function () {
|
||||
return Q();
|
||||
};
|
||||
|
||||
GenericBuilder.prototype.build = function() {
|
||||
GenericBuilder.prototype.build = function () {
|
||||
events.emit('log', 'Skipping build...');
|
||||
return Q(null);
|
||||
};
|
||||
|
||||
GenericBuilder.prototype.clean = function() {
|
||||
GenericBuilder.prototype.clean = function () {
|
||||
return Q();
|
||||
};
|
||||
|
||||
GenericBuilder.prototype.findOutputApks = function(build_type, arch) {
|
||||
GenericBuilder.prototype.findOutputApks = function (build_type, arch) {
|
||||
var self = this;
|
||||
return Object.keys(this.binDirs)
|
||||
.reduce(function (result, builderName) {
|
||||
return Object.keys(this.binDirs).reduce(function (result, builderName) {
|
||||
var binDir = self.binDirs[builderName];
|
||||
return result.concat(findOutputApksHelper(binDir, build_type, builderName === 'ant' ? null : arch));
|
||||
}, [])
|
||||
.sort(apkSorter);
|
||||
};
|
||||
|
||||
GenericBuilder.prototype.readProjectProperties = function () {
|
||||
function findAllUniq(data, r) {
|
||||
var s = {};
|
||||
var m;
|
||||
while ((m = r.exec(data))) {
|
||||
s[m[1]] = 1;
|
||||
}
|
||||
return Object.keys(s);
|
||||
}
|
||||
|
||||
var data = fs.readFileSync(path.join(this.root, 'project.properties'), 'utf8');
|
||||
return {
|
||||
libs: findAllUniq(data, /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg),
|
||||
gradleIncludes: findAllUniq(data, /^\s*cordova\.gradle\.include\.\d+=(.*)(?:\s|$)/mg),
|
||||
systemLibs: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=(.*)(?:\s|$)/mg)
|
||||
};
|
||||
};
|
||||
|
||||
GenericBuilder.prototype.extractRealProjectNameFromManifest = function () {
|
||||
var manifestPath = path.join(this.root, 'AndroidManifest.xml');
|
||||
var manifestData = fs.readFileSync(manifestPath, 'utf8');
|
||||
var m = /<manifest[\s\S]*?package\s*=\s*"(.*?)"/i.exec(manifestData);
|
||||
if (!m) {
|
||||
throw new CordovaError('Could not find package name in ' + manifestPath);
|
||||
}
|
||||
|
||||
var packageName=m[1];
|
||||
var lastDotIndex = packageName.lastIndexOf('.');
|
||||
return packageName.substring(lastDotIndex + 1);
|
||||
}, []).sort(apkSorter);
|
||||
};
|
||||
|
||||
module.exports = GenericBuilder;
|
||||
|
||||
function apkSorter(fileA, fileB) {
|
||||
function apkSorter (fileA, fileB) {
|
||||
// De-prioritize arch specific builds
|
||||
var archSpecificRE = /-x86|-arm/;
|
||||
if (archSpecificRE.exec(fileA)) {
|
||||
return 1;
|
||||
} else if (archSpecificRE.exec(fileB)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// De-prioritize unsigned builds
|
||||
var unsignedRE = /-unsigned/;
|
||||
if (unsignedRE.exec(fileA)) {
|
||||
@ -101,16 +73,22 @@ function apkSorter(fileA, fileB) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
var timeDiff = fs.statSync(fileA).mtime - fs.statSync(fileB).mtime;
|
||||
var timeDiff = fs.statSync(fileB).mtime - fs.statSync(fileA).mtime;
|
||||
return timeDiff === 0 ? fileA.length - fileB.length : timeDiff;
|
||||
}
|
||||
|
||||
function findOutputApksHelper(dir, build_type, arch) {
|
||||
function findOutputApksHelper (dir, build_type, arch) {
|
||||
var shellSilent = shell.config.silent;
|
||||
shell.config.silent = true;
|
||||
|
||||
var ret = shell.ls(path.join(dir, '*.apk'))
|
||||
.filter(function(candidate) {
|
||||
// list directory recursively
|
||||
var ret = shell.ls('-R', dir).map(function (file) {
|
||||
// ls does not include base directory
|
||||
return path.join(dir, file);
|
||||
}).filter(function (file) {
|
||||
// find all APKs
|
||||
return file.match(/\.apk?$/i);
|
||||
}).filter(function (candidate) {
|
||||
var apkName = path.basename(candidate);
|
||||
// Need to choose between release and debug .apk.
|
||||
if (build_type === 'debug') {
|
||||
@ -120,8 +98,7 @@ function findOutputApksHelper(dir, build_type, arch) {
|
||||
return /-release/.exec(apkName) && !/-unaligned/.exec(apkName);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.sort(apkSorter);
|
||||
}).sort(apkSorter);
|
||||
|
||||
shellSilent = shellSilent;
|
||||
|
||||
@ -131,15 +108,15 @@ function findOutputApksHelper(dir, build_type, arch) {
|
||||
// Assume arch-specific build if newest apk has -x86 or -arm.
|
||||
var archSpecific = !!/-x86|-arm/.exec(path.basename(ret[0]));
|
||||
// And show only arch-specific ones (or non-arch-specific)
|
||||
ret = ret.filter(function(p) {
|
||||
/*jshint -W018 */
|
||||
return !!/-x86|-arm/.exec(path.basename(p)) == archSpecific;
|
||||
/*jshint +W018 */
|
||||
ret = ret.filter(function (p) {
|
||||
/* jshint -W018 */
|
||||
return !!/-x86|-arm/.exec(path.basename(p)) === archSpecific;
|
||||
/* jshint +W018 */
|
||||
});
|
||||
|
||||
if (archSpecific && ret.length > 1 && arch) {
|
||||
ret = ret.filter(function(p) {
|
||||
return path.basename(p).indexOf('-' + arch) != -1;
|
||||
ret = ret.filter(function (p) {
|
||||
return path.basename(p).indexOf('-' + arch) !== -1;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ var fs = require('fs');
|
||||
var util = require('util');
|
||||
var path = require('path');
|
||||
var shell = require('shelljs');
|
||||
var spawn = require('cordova-common').superspawn.spawn;
|
||||
var superspawn = require('cordova-common').superspawn;
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
var check_reqs = require('../check_reqs');
|
||||
|
||||
@ -37,15 +37,15 @@ var TEMPLATE =
|
||||
function GradleBuilder (projectRoot) {
|
||||
GenericBuilder.call(this, projectRoot);
|
||||
|
||||
this.binDirs = {gradle: this.binDirs.gradle};
|
||||
this.binDirs = { gradle: this.binDirs.gradle };
|
||||
}
|
||||
|
||||
util.inherits(GradleBuilder, GenericBuilder);
|
||||
|
||||
GradleBuilder.prototype.getArgs = function(cmd, opts) {
|
||||
if (cmd == 'release') {
|
||||
GradleBuilder.prototype.getArgs = function (cmd, opts) {
|
||||
if (cmd === 'release') {
|
||||
cmd = 'cdvBuildRelease';
|
||||
} else if (cmd == 'debug') {
|
||||
} else if (cmd === 'debug') {
|
||||
cmd = 'cdvBuildDebug';
|
||||
}
|
||||
var args = [cmd, '-b', path.join(this.root, 'build.gradle')];
|
||||
@ -69,46 +69,89 @@ GradleBuilder.prototype.getArgs = function(cmd, opts) {
|
||||
* This returns a promise
|
||||
*/
|
||||
|
||||
GradleBuilder.prototype.runGradleWrapper = function(gradle_cmd) {
|
||||
GradleBuilder.prototype.runGradleWrapper = function (gradle_cmd, gradle_file) {
|
||||
var gradlePath = path.join(this.root, 'gradlew');
|
||||
var wrapperGradle = path.join(this.root, 'wrapper.gradle');
|
||||
if(fs.existsSync(gradlePath)) {
|
||||
//Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
|
||||
gradle_file = path.join(this.root, (gradle_file || 'wrapper.gradle'));
|
||||
if (fs.existsSync(gradlePath)) {
|
||||
// Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
|
||||
} else {
|
||||
return spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], {stdio: 'inherit'});
|
||||
return superspawn.spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', gradle_file], { stdio: 'pipe' })
|
||||
.progress(function (stdio) {
|
||||
suppressJavaOptionsInfo(stdio);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* We need to kill this in a fire.
|
||||
*/
|
||||
|
||||
GradleBuilder.prototype.readProjectProperties = function () {
|
||||
function findAllUniq (data, r) {
|
||||
var s = {};
|
||||
var m;
|
||||
while ((m = r.exec(data))) {
|
||||
s[m[1]] = 1;
|
||||
}
|
||||
return Object.keys(s);
|
||||
}
|
||||
|
||||
var data = fs.readFileSync(path.join(this.root, 'project.properties'), 'utf8');
|
||||
return {
|
||||
libs: findAllUniq(data, /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg),
|
||||
gradleIncludes: findAllUniq(data, /^\s*cordova\.gradle\.include\.\d+=(.*)(?:\s|$)/mg),
|
||||
systemLibs: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=(.*)(?:\s|$)/mg)
|
||||
};
|
||||
};
|
||||
|
||||
GradleBuilder.prototype.extractRealProjectNameFromManifest = function () {
|
||||
var manifestPath = path.join(this.root, 'AndroidManifest.xml');
|
||||
var manifestData = fs.readFileSync(manifestPath, 'utf8');
|
||||
var m = /<manifest[\s\S]*?package\s*=\s*"(.*?)"/i.exec(manifestData);
|
||||
if (!m) {
|
||||
throw new CordovaError('Could not find package name in ' + manifestPath);
|
||||
}
|
||||
|
||||
var packageName = m[1];
|
||||
var lastDotIndex = packageName.lastIndexOf('.');
|
||||
return packageName.substring(lastDotIndex + 1);
|
||||
};
|
||||
|
||||
// Makes the project buildable, minus the gradle wrapper.
|
||||
GradleBuilder.prototype.prepBuildFiles = function() {
|
||||
GradleBuilder.prototype.prepBuildFiles = function () {
|
||||
// Update the version of build.gradle in each dependent library.
|
||||
var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle');
|
||||
var propertiesObj = this.readProjectProperties();
|
||||
var subProjects = propertiesObj.libs;
|
||||
var checkAndCopy = function(subProject, root) {
|
||||
var subProjectGradle = path.join(root, subProject, 'build.gradle');
|
||||
// This is the future-proof way of checking if a file exists
|
||||
// This must be synchronous to satisfy a Travis test
|
||||
try {
|
||||
fs.accessSync(subProjectGradle, fs.F_OK);
|
||||
} catch (e) {
|
||||
shell.cp('-f', pluginBuildGradle, subProjectGradle);
|
||||
}
|
||||
|
||||
// Check and copy the gradle file into the subproject.
|
||||
// Called by the loop below this function def.
|
||||
var checkAndCopy = function (subProject, root) {
|
||||
var subProjectGradle = path.join(root, subProject, 'build.gradle');
|
||||
// This is the future-proof way of checking if a file exists
|
||||
// This must be synchronous to satisfy a Travis test
|
||||
try {
|
||||
fs.accessSync(subProjectGradle, fs.F_OK);
|
||||
} catch (e) {
|
||||
shell.cp('-f', pluginBuildGradle, subProjectGradle);
|
||||
}
|
||||
};
|
||||
|
||||
// Some dependencies on Android don't use gradle, or don't have default
|
||||
// gradle files. This copies a dummy gradle file into them
|
||||
for (var i = 0; i < subProjects.length; ++i) {
|
||||
if (subProjects[i] !== 'CordovaLib') {
|
||||
checkAndCopy(subProjects[i], this.root);
|
||||
if (subProjects[i] !== 'CordovaLib' && subProjects[i] !== 'app') {
|
||||
checkAndCopy(subProjects[i], this.root);
|
||||
}
|
||||
}
|
||||
|
||||
var name = this.extractRealProjectNameFromManifest();
|
||||
//Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
|
||||
var settingsGradlePaths = subProjects.map(function(p){
|
||||
var realDir=p.replace(/[/\\]/g, ':');
|
||||
var libName=realDir.replace(name+'-','');
|
||||
var str='include ":'+libName+'"\n';
|
||||
if(realDir.indexOf(name+'-')!==-1)
|
||||
str+='project(":'+libName+'").projectDir = new File("'+p+'")\n';
|
||||
// Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
|
||||
var settingsGradlePaths = subProjects.map(function (p) {
|
||||
var realDir = p.replace(/[/\\]/g, ':');
|
||||
var libName = realDir.replace(name + '-', '');
|
||||
var str = 'include ":' + libName + '"\n';
|
||||
if (realDir.indexOf(name + '-') !== -1) { str += 'project(":' + libName + '").projectDir = new File("' + p + '")\n'; }
|
||||
return str;
|
||||
});
|
||||
|
||||
@ -120,30 +163,33 @@ GradleBuilder.prototype.prepBuildFiles = function() {
|
||||
var buildGradle = fs.readFileSync(path.join(this.root, 'build.gradle'), 'utf8');
|
||||
var depsList = '';
|
||||
var root = this.root;
|
||||
var insertExclude = function(p) {
|
||||
var gradlePath = path.join(root, p, 'build.gradle');
|
||||
var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
|
||||
if(projectGradleFile.indexOf('CordovaLib') != -1) {
|
||||
|
||||
// Cordova Plugins can be written as library modules that would use Cordova as a
|
||||
// dependency. Because we need to make sure that Cordova is compiled only once for
|
||||
// dexing, we make sure to exclude CordovaLib from these modules
|
||||
var insertExclude = function (p) {
|
||||
var gradlePath = path.join(root, p, 'build.gradle');
|
||||
var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
|
||||
if (projectGradleFile.indexOf('CordovaLib') !== -1) {
|
||||
depsList += '{\n exclude module:("CordovaLib")\n }\n';
|
||||
}
|
||||
else {
|
||||
depsList +='\n';
|
||||
}
|
||||
} else {
|
||||
depsList += '\n';
|
||||
}
|
||||
};
|
||||
subProjects.forEach(function(p) {
|
||||
|
||||
subProjects.forEach(function (p) {
|
||||
console.log('Subproject Path: ' + p);
|
||||
var libName=p.replace(/[/\\]/g, ':').replace(name+'-','');
|
||||
depsList += ' debugCompile(project(path: "' + libName + '", configuration: "debug"))';
|
||||
insertExclude(p);
|
||||
depsList += ' releaseCompile(project(path: "' + libName + '", configuration: "release"))';
|
||||
var libName = p.replace(/[/\\]/g, ':').replace(name + '-', '');
|
||||
depsList += ' implementation(project(path: "' + libName + '"))';
|
||||
insertExclude(p);
|
||||
});
|
||||
|
||||
// For why we do this mapping: https://issues.apache.org/jira/browse/CB-8390
|
||||
var SYSTEM_LIBRARY_MAPPINGS = [
|
||||
[/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'],
|
||||
[/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+']
|
||||
];
|
||||
propertiesObj.systemLibs.forEach(function(p) {
|
||||
propertiesObj.systemLibs.forEach(function (p) {
|
||||
var mavenRef;
|
||||
// It's already in gradle form if it has two ':'s
|
||||
if (/:.*:/.exec(p)) {
|
||||
@ -162,23 +208,25 @@ GradleBuilder.prototype.prepBuildFiles = function() {
|
||||
}
|
||||
depsList += ' compile "' + mavenRef + '"\n';
|
||||
});
|
||||
|
||||
// This code is dangerous and actually writes gradle declarations directly into the build.gradle
|
||||
// Try not to mess with this if possible
|
||||
buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + ' $2');
|
||||
var includeList = '';
|
||||
propertiesObj.gradleIncludes.forEach(function(includePath) {
|
||||
propertiesObj.gradleIncludes.forEach(function (includePath) {
|
||||
includeList += 'apply from: "' + includePath + '"\n';
|
||||
});
|
||||
buildGradle = buildGradle.replace(/(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + includeList + '$2');
|
||||
fs.writeFileSync(path.join(this.root, 'build.gradle'), buildGradle);
|
||||
};
|
||||
|
||||
GradleBuilder.prototype.prepEnv = function(opts) {
|
||||
GradleBuilder.prototype.prepEnv = function (opts) {
|
||||
var self = this;
|
||||
return check_reqs.check_gradle()
|
||||
.then(function(gradlePath) {
|
||||
return check_reqs.check_gradle().then(function (gradlePath) {
|
||||
return self.runGradleWrapper(gradlePath);
|
||||
}).then(function() {
|
||||
return self.prepBuildFiles();
|
||||
}).then(function() {
|
||||
}).then(function () {
|
||||
return self.prepBuildFiles();
|
||||
}).then(function () {
|
||||
// We now copy the gradle out of the framework
|
||||
// This is a dirty patch to get the build working
|
||||
/*
|
||||
@ -198,12 +246,12 @@ GradleBuilder.prototype.prepEnv = function(opts) {
|
||||
// If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with.
|
||||
// For some reason, using ^ and $ don't work. This does the job, though.
|
||||
var distributionUrlRegex = /distributionUrl.*zip/;
|
||||
/*jshint -W069 */
|
||||
var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-3.3-all.zip';
|
||||
/*jshint +W069 */
|
||||
/* jshint -W069 */
|
||||
var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-4.1-all.zip';
|
||||
/* jshint +W069 */
|
||||
var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties');
|
||||
shell.chmod('u+w', gradleWrapperPropertiesPath);
|
||||
shell.sed('-i', distributionUrlRegex, 'distributionUrl='+distributionUrl, gradleWrapperPropertiesPath);
|
||||
shell.sed('-i', distributionUrlRegex, 'distributionUrl=' + distributionUrl, gradleWrapperPropertiesPath);
|
||||
|
||||
var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
|
||||
var propertiesFilePath = path.join(self.root, propertiesFile);
|
||||
@ -219,53 +267,37 @@ GradleBuilder.prototype.prepEnv = function(opts) {
|
||||
* Builds the project with gradle.
|
||||
* Returns a promise.
|
||||
*/
|
||||
GradleBuilder.prototype.build = function(opts) {
|
||||
GradleBuilder.prototype.build = function (opts) {
|
||||
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 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;
|
||||
return superspawn.spawn(wrapper, args, { stdio: 'pipe' })
|
||||
.progress(function (stdio) {
|
||||
suppressJavaOptionsInfo(stdio);
|
||||
}).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);
|
||||
});
|
||||
}
|
||||
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);
|
||||
});
|
||||
return Q.reject(error);
|
||||
});
|
||||
};
|
||||
|
||||
GradleBuilder.prototype.clean = function(opts) {
|
||||
GradleBuilder.prototype.clean = function (opts) {
|
||||
var builder = this;
|
||||
var wrapper = path.join(this.root, 'gradlew');
|
||||
var args = builder.getArgs('clean', opts);
|
||||
return Q().then(function() {
|
||||
return spawn(wrapper, args, {stdio: 'inherit'});
|
||||
})
|
||||
.then(function () {
|
||||
return Q().then(function () {
|
||||
return superspawn.spawn(wrapper, args, { stdio: 'inherit' });
|
||||
}).then(function () {
|
||||
shell.rm('-rf', path.join(builder.root, 'out'));
|
||||
|
||||
['debug', 'release'].forEach(function(config) {
|
||||
['debug', 'release'].forEach(function (config) {
|
||||
var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES);
|
||||
if(isAutoGenerated(propertiesFilePath)){
|
||||
if (isAutoGenerated(propertiesFilePath)) {
|
||||
shell.rm('-f', propertiesFilePath);
|
||||
}
|
||||
});
|
||||
@ -274,6 +306,25 @@ GradleBuilder.prototype.clean = function(opts) {
|
||||
|
||||
module.exports = GradleBuilder;
|
||||
|
||||
function isAutoGenerated(file) {
|
||||
function suppressJavaOptionsInfo (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);
|
||||
}
|
||||
}
|
||||
|
||||
function isAutoGenerated (file) {
|
||||
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
|
||||
}
|
||||
|
302
DynamicBibleIonic/platforms/android/cordova/lib/builders/StudioBuilder.js
vendored
Normal file
@ -0,0 +1,302 @@
|
||||
/*
|
||||
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 Q = require('q');
|
||||
var fs = require('fs');
|
||||
var util = require('util');
|
||||
var path = require('path');
|
||||
var shell = require('shelljs');
|
||||
var spawn = require('cordova-common').superspawn.spawn;
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
var check_reqs = require('../check_reqs');
|
||||
|
||||
var GenericBuilder = require('./GenericBuilder');
|
||||
|
||||
var MARKER = 'YOUR CHANGES WILL BE ERASED!';
|
||||
var SIGNING_PROPERTIES = '-signing.properties';
|
||||
var TEMPLATE =
|
||||
'# This file is automatically generated.\n' +
|
||||
'# Do not modify this file -- ' + MARKER + '\n';
|
||||
|
||||
function StudioBuilder (projectRoot) {
|
||||
GenericBuilder.call(this, projectRoot);
|
||||
|
||||
this.binDirs = {gradle: this.binDirs.studio};
|
||||
}
|
||||
|
||||
util.inherits(StudioBuilder, GenericBuilder);
|
||||
|
||||
StudioBuilder.prototype.getArgs = function (cmd, opts) {
|
||||
if (cmd === 'release') {
|
||||
cmd = 'cdvBuildRelease';
|
||||
} else if (cmd === 'debug') {
|
||||
cmd = 'cdvBuildDebug';
|
||||
}
|
||||
var args = [cmd, '-b', path.join(this.root, 'build.gradle')];
|
||||
if (opts.arch) {
|
||||
args.push('-PcdvBuildArch=' + opts.arch);
|
||||
}
|
||||
|
||||
// 10 seconds -> 6 seconds
|
||||
args.push('-Dorg.gradle.daemon=true');
|
||||
// to allow dex in process
|
||||
args.push('-Dorg.gradle.jvmargs=-Xmx2048m');
|
||||
// allow NDK to be used - required by Gradle 1.5 plugin
|
||||
args.push('-Pandroid.useDeprecatedNdk=true');
|
||||
args.push.apply(args, opts.extraArgs);
|
||||
// Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
|
||||
// args.push('-Dorg.gradle.parallel=true');
|
||||
return args;
|
||||
};
|
||||
|
||||
/*
|
||||
* This returns a promise
|
||||
*/
|
||||
|
||||
StudioBuilder.prototype.runGradleWrapper = function (gradle_cmd) {
|
||||
var gradlePath = path.join(this.root, 'gradlew');
|
||||
var wrapperGradle = path.join(this.root, 'wrapper.gradle');
|
||||
if (fs.existsSync(gradlePath)) {
|
||||
// Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
|
||||
} else {
|
||||
return spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], {stdio: 'inherit'});
|
||||
}
|
||||
};
|
||||
|
||||
StudioBuilder.prototype.readProjectProperties = function () {
|
||||
|
||||
function findAllUniq (data, r) {
|
||||
var s = {};
|
||||
var m;
|
||||
while ((m = r.exec(data))) {
|
||||
s[m[1]] = 1;
|
||||
}
|
||||
return Object.keys(s);
|
||||
}
|
||||
|
||||
var data = fs.readFileSync(path.join(this.root, 'project.properties'), 'utf8');
|
||||
return {
|
||||
libs: findAllUniq(data, /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg),
|
||||
gradleIncludes: findAllUniq(data, /^\s*cordova\.gradle\.include\.\d+=(.*)(?:\s|$)/mg),
|
||||
systemLibs: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=(.*)(?:\s|$)/mg)
|
||||
};
|
||||
};
|
||||
|
||||
StudioBuilder.prototype.extractRealProjectNameFromManifest = function () {
|
||||
var manifestPath = path.join(this.root, 'app', 'src', 'main', 'AndroidManifest.xml');
|
||||
var manifestData = fs.readFileSync(manifestPath, 'utf8');
|
||||
var m = /<manifest[\s\S]*?package\s*=\s*"(.*?)"/i.exec(manifestData);
|
||||
if (!m) {
|
||||
throw new CordovaError('Could not find package name in ' + manifestPath);
|
||||
}
|
||||
|
||||
var packageName = m[1];
|
||||
var lastDotIndex = packageName.lastIndexOf('.');
|
||||
return packageName.substring(lastDotIndex + 1);
|
||||
};
|
||||
|
||||
// Makes the project buildable, minus the gradle wrapper.
|
||||
StudioBuilder.prototype.prepBuildFiles = function () {
|
||||
// Update the version of build.gradle in each dependent library.
|
||||
var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle');
|
||||
var propertiesObj = this.readProjectProperties();
|
||||
var subProjects = propertiesObj.libs;
|
||||
|
||||
// Check and copy the gradle file into the subproject
|
||||
// Called by the loop before this function def
|
||||
|
||||
var checkAndCopy = function (subProject, root) {
|
||||
var subProjectGradle = path.join(root, subProject, 'build.gradle');
|
||||
// This is the future-proof way of checking if a file exists
|
||||
// This must be synchronous to satisfy a Travis test
|
||||
try {
|
||||
fs.accessSync(subProjectGradle, fs.F_OK);
|
||||
} catch (e) {
|
||||
shell.cp('-f', pluginBuildGradle, subProjectGradle);
|
||||
}
|
||||
};
|
||||
|
||||
for (var i = 0; i < subProjects.length; ++i) {
|
||||
if (subProjects[i] !== 'CordovaLib') {
|
||||
checkAndCopy(subProjects[i], this.root);
|
||||
}
|
||||
}
|
||||
var name = this.extractRealProjectNameFromManifest();
|
||||
// Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
|
||||
var settingsGradlePaths = subProjects.map(function (p) {
|
||||
var realDir = p.replace(/[/\\]/g, ':');
|
||||
var libName = realDir.replace(name + '-', '');
|
||||
var str = 'include ":' + libName + '"\n';
|
||||
if (realDir.indexOf(name + '-') !== -1) {
|
||||
str += 'project(":' + libName + '").projectDir = new File("' + p + '")\n';
|
||||
}
|
||||
return str;
|
||||
});
|
||||
|
||||
fs.writeFileSync(path.join(this.root, 'settings.gradle'),
|
||||
'// GENERATED FILE - DO NOT EDIT\n' +
|
||||
'include ":"\n' + settingsGradlePaths.join(''));
|
||||
|
||||
// Update dependencies within build.gradle.
|
||||
var buildGradle = fs.readFileSync(path.join(this.root, 'app', 'build.gradle'), 'utf8');
|
||||
var depsList = '';
|
||||
var root = this.root;
|
||||
var insertExclude = function (p) {
|
||||
var gradlePath = path.join(root, p, 'build.gradle');
|
||||
var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
|
||||
if (projectGradleFile.indexOf('CordovaLib') !== -1) {
|
||||
depsList += '{\n exclude module:("CordovaLib")\n }\n';
|
||||
} else {
|
||||
depsList += '\n';
|
||||
}
|
||||
};
|
||||
subProjects.forEach(function (p) {
|
||||
console.log('Subproject Path: ' + p);
|
||||
var libName = p.replace(/[/\\]/g, ':').replace(name + '-', '');
|
||||
if (libName !== 'app') {
|
||||
depsList += ' implementation(project(path: ":' + libName + '"))';
|
||||
insertExclude(p);
|
||||
}
|
||||
});
|
||||
// For why we do this mapping: https://issues.apache.org/jira/browse/CB-8390
|
||||
var SYSTEM_LIBRARY_MAPPINGS = [
|
||||
[/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'],
|
||||
[/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+']
|
||||
];
|
||||
|
||||
propertiesObj.systemLibs.forEach(function (p) {
|
||||
var mavenRef;
|
||||
// It's already in gradle form if it has two ':'s
|
||||
if (/:.*:/.exec(p)) {
|
||||
mavenRef = p;
|
||||
} else {
|
||||
for (var i = 0; i < SYSTEM_LIBRARY_MAPPINGS.length; ++i) {
|
||||
var pair = SYSTEM_LIBRARY_MAPPINGS[i];
|
||||
if (pair[0].exec(p)) {
|
||||
mavenRef = p.replace(pair[0], pair[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mavenRef) {
|
||||
throw new CordovaError('Unsupported system library (does not work with gradle): ' + p);
|
||||
}
|
||||
}
|
||||
depsList += ' compile "' + mavenRef + '"\n';
|
||||
});
|
||||
|
||||
buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + ' $2');
|
||||
var includeList = '';
|
||||
|
||||
propertiesObj.gradleIncludes.forEach(function (includePath) {
|
||||
includeList += 'apply from: "../' + includePath + '"\n';
|
||||
});
|
||||
buildGradle = buildGradle.replace(/(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + includeList + '$2');
|
||||
// This needs to be stored in the app gradle, not the root grade
|
||||
fs.writeFileSync(path.join(this.root, 'app', 'build.gradle'), buildGradle);
|
||||
};
|
||||
|
||||
StudioBuilder.prototype.prepEnv = function (opts) {
|
||||
var self = this;
|
||||
return check_reqs.check_gradle()
|
||||
.then(function (gradlePath) {
|
||||
return self.runGradleWrapper(gradlePath);
|
||||
}).then(function () {
|
||||
return self.prepBuildFiles();
|
||||
}).then(function () {
|
||||
// If the gradle distribution URL is set, make sure it points to version we want.
|
||||
// If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with.
|
||||
// For some reason, using ^ and $ don't work. This does the job, though.
|
||||
var distributionUrlRegex = /distributionUrl.*zip/;
|
||||
var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-4.1-all.zip';
|
||||
var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties');
|
||||
shell.chmod('u+w', gradleWrapperPropertiesPath);
|
||||
shell.sed('-i', distributionUrlRegex, 'distributionUrl=' + distributionUrl, gradleWrapperPropertiesPath);
|
||||
|
||||
var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
|
||||
var propertiesFilePath = path.join(self.root, propertiesFile);
|
||||
if (opts.packageInfo) {
|
||||
fs.writeFileSync(propertiesFilePath, TEMPLATE + opts.packageInfo.toProperties());
|
||||
} else if (isAutoGenerated(propertiesFilePath)) {
|
||||
shell.rm('-f', propertiesFilePath);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* Builds the project with gradle.
|
||||
* Returns a promise.
|
||||
*/
|
||||
StudioBuilder.prototype.build = function (opts) {
|
||||
var wrapper = path.join(this.root, 'gradlew');
|
||||
var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
|
||||
|
||||
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);
|
||||
});
|
||||
};
|
||||
|
||||
StudioBuilder.prototype.clean = function (opts) {
|
||||
var builder = this;
|
||||
var wrapper = path.join(this.root, 'gradlew');
|
||||
var args = builder.getArgs('clean', opts);
|
||||
return Q().then(function () {
|
||||
return spawn(wrapper, args, {stdio: 'inherit'});
|
||||
})
|
||||
.then(function () {
|
||||
shell.rm('-rf', path.join(builder.root, 'out'));
|
||||
|
||||
['debug', 'release'].forEach(function (config) {
|
||||
var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES);
|
||||
if (isAutoGenerated(propertiesFilePath)) {
|
||||
shell.rm('-f', propertiesFilePath);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = StudioBuilder;
|
||||
|
||||
function isAutoGenerated (file) {
|
||||
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
|
||||
}
|
@ -20,8 +20,8 @@
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
|
||||
var knownBuilders = {
|
||||
ant: 'AntBuilder',
|
||||
gradle: 'GradleBuilder',
|
||||
studio: 'StudioBuilder',
|
||||
none: 'GenericBuilder'
|
||||
};
|
||||
|
||||
@ -35,8 +35,7 @@ var knownBuilders = {
|
||||
* @return {Builder} A builder instance for specified build type.
|
||||
*/
|
||||
module.exports.getBuilder = function (builderType, projectRoot) {
|
||||
if (!knownBuilders[builderType])
|
||||
throw new CordovaError('Builder ' + builderType + ' is not supported.');
|
||||
if (!knownBuilders[builderType]) { throw new CordovaError('Builder ' + builderType + ' is not supported.'); }
|
||||
|
||||
try {
|
||||
var Builder = require('./' + knownBuilders[builderType]);
|
||||
|
@ -21,19 +21,19 @@
|
||||
|
||||
/* jshint sub:true */
|
||||
|
||||
var shelljs = require('shelljs'),
|
||||
child_process = require('child_process'),
|
||||
Q = require('q'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
os = require('os'),
|
||||
REPO_ROOT = path.join(__dirname, '..', '..', '..', '..'),
|
||||
PROJECT_ROOT = path.join(__dirname, '..', '..');
|
||||
var shelljs = require('shelljs');
|
||||
var child_process = require('child_process');
|
||||
var Q = require('q');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var os = require('os');
|
||||
var REPO_ROOT = path.join(__dirname, '..', '..', '..', '..');
|
||||
var PROJECT_ROOT = path.join(__dirname, '..', '..');
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
var superspawn = require('cordova-common').superspawn;
|
||||
var android_sdk = require('./android_sdk');
|
||||
|
||||
function forgivingWhichSync(cmd) {
|
||||
function forgivingWhichSync (cmd) {
|
||||
try {
|
||||
return fs.realpathSync(shelljs.which(cmd));
|
||||
} catch (e) {
|
||||
@ -41,9 +41,9 @@ function forgivingWhichSync(cmd) {
|
||||
}
|
||||
}
|
||||
|
||||
function tryCommand(cmd, errMsg, catchStderr) {
|
||||
function tryCommand (cmd, errMsg, catchStderr) {
|
||||
var d = Q.defer();
|
||||
child_process.exec(cmd, function(err, stdout, stderr) {
|
||||
child_process.exec(cmd, function (err, stdout, stderr) {
|
||||
if (err) d.reject(new CordovaError(errMsg));
|
||||
// Sometimes it is necessary to return an stderr instead of stdout in case of success, since
|
||||
// some commands prints theirs output to stderr instead of stdout. 'javac' is the example
|
||||
@ -52,18 +52,18 @@ function tryCommand(cmd, errMsg, catchStderr) {
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
module.exports.isWindows = function() {
|
||||
return (os.platform() == 'win32');
|
||||
module.exports.isWindows = function () {
|
||||
return (os.platform() === 'win32');
|
||||
};
|
||||
|
||||
module.exports.isDarwin = function() {
|
||||
return (os.platform() == 'darwin');
|
||||
module.exports.isDarwin = function () {
|
||||
return (os.platform() === 'darwin');
|
||||
};
|
||||
|
||||
// Get valid target from framework/project.properties if run from this repo
|
||||
// Otherwise get target from project.properties file within a generated cordova-android project
|
||||
module.exports.get_target = function() {
|
||||
function extractFromFile(filePath) {
|
||||
module.exports.get_target = function () {
|
||||
function extractFromFile (filePath) {
|
||||
var target = shelljs.grep(/\btarget=/, filePath);
|
||||
if (!target) {
|
||||
throw new Error('Could not find android target within: ' + filePath);
|
||||
@ -83,77 +83,82 @@ module.exports.get_target = function() {
|
||||
};
|
||||
|
||||
// Returns a promise. Called only by build and clean commands.
|
||||
module.exports.check_ant = function() {
|
||||
return superspawn.spawn('ant', ['-version'])
|
||||
.then(function(output) {
|
||||
module.exports.check_ant = function () {
|
||||
return superspawn.spawn('ant', ['-version']).then(function (output) {
|
||||
// Parse Ant version from command output
|
||||
return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1];
|
||||
}).catch(function(err) {
|
||||
throw new CordovaError('Failed to run `ant -version`. Make sure you have `ant` on your $PATH.');
|
||||
}).catch(function (err) {
|
||||
if (err) {
|
||||
throw new CordovaError('Failed to run `ant -version`. Make sure you have `ant` on your $PATH.');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.get_gradle_wrapper = function() {
|
||||
module.exports.get_gradle_wrapper = function () {
|
||||
var androidStudioPath;
|
||||
var i = 0;
|
||||
var foundStudio = false;
|
||||
var program_dir;
|
||||
if (module.exports.isDarwin()) {
|
||||
program_dir = fs.readdirSync('/Applications');
|
||||
while (i < program_dir.length && !foundStudio) {
|
||||
if (program_dir[i].startsWith('Android Studio')) {
|
||||
//TODO: Check for a specific Android Studio version, make sure it's not Canary
|
||||
androidStudioPath = path.join('/Applications', program_dir[i], 'Contents', 'gradle');
|
||||
foundStudio = true;
|
||||
} else { ++i; }
|
||||
}
|
||||
} else if (module.exports.isWindows()) {
|
||||
var androidPath = path.join(process.env['ProgramFiles'], 'Android') + '/';
|
||||
if (fs.existsSync(androidPath)) {
|
||||
program_dir = fs.readdirSync(androidPath);
|
||||
while (i < program_dir.length && !foundStudio) {
|
||||
if (program_dir[i].startsWith('Android Studio')) {
|
||||
foundStudio = true;
|
||||
androidStudioPath = path.join(process.env['ProgramFiles'], 'Android', program_dir[i], 'gradle');
|
||||
} else { ++i; }
|
||||
// OK, This hack only works on Windows, not on Mac OS or Linux. We will be deleting this eventually!
|
||||
if (module.exports.isWindows()) {
|
||||
|
||||
var result = child_process.spawnSync(path.join(__dirname, 'getASPath.bat'));
|
||||
// console.log('result.stdout =' + result.stdout.toString());
|
||||
// console.log('result.stderr =' + result.stderr.toString());
|
||||
|
||||
if (result.stderr.toString().length > 0) {
|
||||
var androidPath = path.join(process.env['ProgramFiles'], 'Android') + '/';
|
||||
if (fs.existsSync(androidPath)) {
|
||||
program_dir = fs.readdirSync(androidPath);
|
||||
while (i < program_dir.length && !foundStudio) {
|
||||
if (program_dir[i].startsWith('Android Studio')) {
|
||||
foundStudio = true;
|
||||
androidStudioPath = path.join(process.env['ProgramFiles'], 'Android', program_dir[i], 'gradle');
|
||||
} else { ++i; }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// console.log('got android studio path from registry');
|
||||
// remove the (os independent) new line char at the end of stdout
|
||||
// add gradle to match the above.
|
||||
androidStudioPath = path.join(result.stdout.toString().split('\r\n')[0], 'gradle');
|
||||
}
|
||||
}
|
||||
|
||||
if (androidStudioPath !== null && fs.existsSync(androidStudioPath)) {
|
||||
var dirs = fs.readdirSync(androidStudioPath);
|
||||
if(dirs[0].split('-')[0] == 'gradle') {
|
||||
if (dirs[0].split('-')[0] === 'gradle') {
|
||||
return path.join(androidStudioPath, dirs[0], 'bin', 'gradle');
|
||||
}
|
||||
} else {
|
||||
//OK, let's try to check for Gradle!
|
||||
// OK, let's try to check for Gradle!
|
||||
return forgivingWhichSync('gradle');
|
||||
}
|
||||
};
|
||||
|
||||
// Returns a promise. Called only by build and clean commands.
|
||||
module.exports.check_gradle = function() {
|
||||
module.exports.check_gradle = function () {
|
||||
var sdkDir = process.env['ANDROID_HOME'];
|
||||
var d = Q.defer();
|
||||
if (!sdkDir)
|
||||
if (!sdkDir) {
|
||||
return Q.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Could not find Android SDK directory.\n' +
|
||||
'Might need to install Android SDK or set up \'ANDROID_HOME\' env variable.'));
|
||||
}
|
||||
|
||||
var gradlePath = module.exports.get_gradle_wrapper();
|
||||
if (gradlePath.length !== 0)
|
||||
d.resolve(gradlePath);
|
||||
else
|
||||
if (gradlePath.length !== 0) { d.resolve(gradlePath); } else {
|
||||
d.reject(new CordovaError('Could not find an installed version of Gradle either in Android Studio,\n' +
|
||||
'or on your system to install the gradle wrapper. Please include gradle \n' +
|
||||
'in your path, or install Android Studio'));
|
||||
}
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
// Returns a promise.
|
||||
module.exports.check_java = function() {
|
||||
module.exports.check_java = function () {
|
||||
var javacPath = forgivingWhichSync('javac');
|
||||
var hasJavaHome = !!process.env['JAVA_HOME'];
|
||||
return Q().then(function() {
|
||||
return Q().then(function () {
|
||||
if (hasJavaHome) {
|
||||
// Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh).
|
||||
if (!javacPath) {
|
||||
@ -163,13 +168,14 @@ module.exports.check_java = function() {
|
||||
if (javacPath) {
|
||||
// OS X has a command for finding JAVA_HOME.
|
||||
var find_java = '/usr/libexec/java_home';
|
||||
var default_java_error_msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting setting it manually.';
|
||||
var default_java_error_msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting it manually.';
|
||||
if (fs.existsSync(find_java)) {
|
||||
return superspawn.spawn(find_java)
|
||||
.then(function(stdout) {
|
||||
return superspawn.spawn(find_java).then(function (stdout) {
|
||||
process.env['JAVA_HOME'] = stdout.trim();
|
||||
}).catch(function(err) {
|
||||
throw new CordovaError(default_java_error_msg);
|
||||
}).catch(function (err) {
|
||||
if (err) {
|
||||
throw new CordovaError(default_java_error_msg);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// See if we can derive it from javac's location.
|
||||
@ -200,7 +206,7 @@ module.exports.check_java = function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}).then(function() {
|
||||
}).then(function () {
|
||||
var msg =
|
||||
'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';
|
||||
@ -209,9 +215,8 @@ module.exports.check_java = function() {
|
||||
}
|
||||
// We use tryCommand with catchStderr = true, because
|
||||
// javac writes version info to stderr instead of stdout
|
||||
return tryCommand('javac -version', msg, true)
|
||||
.then(function (output) {
|
||||
//Let's check for at least Java 8, and keep it future proof so we can support Java 10
|
||||
return tryCommand('javac -version', msg, true).then(function (output) {
|
||||
// Let's check for at least Java 8, and keep it future proof so we can support Java 10
|
||||
var match = /javac ((?:1\.)(?:[8-9]\.)(?:\d+))|((?:1\.)(?:[1-9]\d+\.)(?:\d+))/i.exec(output);
|
||||
return match && match[1];
|
||||
});
|
||||
@ -219,13 +224,13 @@ module.exports.check_java = function() {
|
||||
};
|
||||
|
||||
// Returns a promise.
|
||||
module.exports.check_android = function() {
|
||||
return Q().then(function() {
|
||||
module.exports.check_android = function () {
|
||||
return Q().then(function () {
|
||||
var androidCmdPath = forgivingWhichSync('android');
|
||||
var adbInPath = forgivingWhichSync('adb');
|
||||
var avdmanagerInPath = forgivingWhichSync('avdmanager');
|
||||
var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
|
||||
function maybeSetAndroidHome(value) {
|
||||
function maybeSetAndroidHome (value) {
|
||||
if (!hasAndroidHome && fs.existsSync(value)) {
|
||||
hasAndroidHome = true;
|
||||
process.env['ANDROID_HOME'] = value;
|
||||
@ -265,10 +270,10 @@ module.exports.check_android = function() {
|
||||
if (androidCmdPath) {
|
||||
parentDir = path.dirname(androidCmdPath);
|
||||
grandParentDir = path.dirname(parentDir);
|
||||
if (path.basename(parentDir) == 'tools' || fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) {
|
||||
if (path.basename(parentDir) === 'tools' || fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) {
|
||||
maybeSetAndroidHome(grandParentDir);
|
||||
} else {
|
||||
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
|
||||
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
|
||||
'Detected \'android\' command at ' + parentDir + ' but no \'tools\' directory found near.\n' +
|
||||
'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'tools directory.');
|
||||
}
|
||||
@ -276,10 +281,10 @@ module.exports.check_android = function() {
|
||||
if (adbInPath) {
|
||||
parentDir = path.dirname(adbInPath);
|
||||
grandParentDir = path.dirname(parentDir);
|
||||
if (path.basename(parentDir) == 'platform-tools') {
|
||||
if (path.basename(parentDir) === 'platform-tools') {
|
||||
maybeSetAndroidHome(grandParentDir);
|
||||
} else {
|
||||
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
|
||||
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
|
||||
'Detected \'adb\' command at ' + parentDir + ' but no \'platform-tools\' directory found near.\n' +
|
||||
'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'platform-tools directory.');
|
||||
}
|
||||
@ -287,17 +292,17 @@ module.exports.check_android = function() {
|
||||
if (avdmanagerInPath) {
|
||||
parentDir = path.dirname(avdmanagerInPath);
|
||||
grandParentDir = path.dirname(parentDir);
|
||||
if (path.basename(parentDir) == 'bin' && path.basename(grandParentDir) == 'tools') {
|
||||
if (path.basename(parentDir) === 'bin' && path.basename(grandParentDir) === 'tools') {
|
||||
maybeSetAndroidHome(path.dirname(grandParentDir));
|
||||
} else {
|
||||
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
|
||||
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
|
||||
'Detected \'avdmanager\' command at ' + parentDir + ' but no \'tools' + path.sep + 'bin\' directory found near.\n' +
|
||||
'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'tools' + path.sep + 'bin directory.');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!process.env['ANDROID_HOME']) {
|
||||
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
|
||||
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
|
||||
'Failed to find \'android\' command in your \'PATH\'. Try update your \'PATH\' to include path to valid SDK directory.');
|
||||
}
|
||||
if (!fs.existsSync(process.env['ANDROID_HOME'])) {
|
||||
@ -330,20 +335,19 @@ module.exports.getAbsoluteAndroidCmd = function () {
|
||||
return cmd.replace(/(\s)/g, '\\$1');
|
||||
};
|
||||
|
||||
module.exports.check_android_target = function(originalError) {
|
||||
module.exports.check_android_target = function (originalError) {
|
||||
// valid_target can look like:
|
||||
// android-19
|
||||
// android-L
|
||||
// Google Inc.:Google APIs:20
|
||||
// Google Inc.:Glass Development Kit Preview:20
|
||||
var desired_api_level = module.exports.get_target();
|
||||
return android_sdk.list_targets()
|
||||
.then(function(targets) {
|
||||
return android_sdk.list_targets().then(function (targets) {
|
||||
if (targets.indexOf(desired_api_level) >= 0) {
|
||||
return targets;
|
||||
}
|
||||
var androidCmd = module.exports.getAbsoluteAndroidCmd();
|
||||
var msg = 'Please install Android target / API level: "' + desired_api_level + '".\n\n' +
|
||||
var msg = 'Please install Android target / API level: "' + desired_api_level + '".\n\n' +
|
||||
'Hint: Open the SDK manager by running: ' + androidCmd + '\n' +
|
||||
'You will require:\n' +
|
||||
'1. "SDK Platform" for API level ' + desired_api_level + '\n' +
|
||||
@ -357,23 +361,21 @@ module.exports.check_android_target = function(originalError) {
|
||||
};
|
||||
|
||||
// Returns a promise.
|
||||
module.exports.run = function() {
|
||||
return Q.all([this.check_java(), this.check_android()])
|
||||
.then(function(values) {
|
||||
console.log('ANDROID_HOME=' + process.env['ANDROID_HOME']);
|
||||
console.log('JAVA_HOME=' + process.env['JAVA_HOME']);
|
||||
module.exports.run = function () {
|
||||
return Q.all([this.check_java(), this.check_android()]).then(function (values) {
|
||||
console.log('ANDROID_HOME=' + process.env['ANDROID_HOME']);
|
||||
console.log('JAVA_HOME=' + process.env['JAVA_HOME']);
|
||||
|
||||
if (!values[0]) {
|
||||
if (!values[0]) {
|
||||
throw new CordovaError('Requirements check failed for JDK 1.8 or greater');
|
||||
}
|
||||
}
|
||||
|
||||
if (!values[1]) {
|
||||
if (!values[1]) {
|
||||
throw new CordovaError('Requirements check failed for Android SDK');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Object thar represents one of requirements for current platform.
|
||||
* @param {String} id The unique identifier for this requirements.
|
||||
@ -387,7 +389,7 @@ var Requirement = function (id, name, version, installed) {
|
||||
this.name = name;
|
||||
this.installed = installed || false;
|
||||
this.metadata = {
|
||||
version: version,
|
||||
version: version
|
||||
};
|
||||
};
|
||||
|
||||
@ -397,7 +399,7 @@ var Requirement = function (id, name, version, installed) {
|
||||
*
|
||||
* @return Promise<Requirement[]> Array of requirements. Due to implementation, promise is always fulfilled.
|
||||
*/
|
||||
module.exports.check_all = function() {
|
||||
module.exports.check_all = function () {
|
||||
|
||||
var requirements = [
|
||||
new Requirement('java', 'Java JDK'),
|
||||
@ -417,15 +419,13 @@ module.exports.check_all = function() {
|
||||
return checkFns.reduce(function (promise, checkFn, idx) {
|
||||
// Update each requirement with results
|
||||
var requirement = requirements[idx];
|
||||
return promise.then(checkFn)
|
||||
.then(function (version) {
|
||||
return promise.then(checkFn).then(function (version) {
|
||||
requirement.installed = true;
|
||||
requirement.metadata.version = version;
|
||||
}, function (err) {
|
||||
requirement.metadata.reason = err instanceof Error ? err.message : err;
|
||||
});
|
||||
}, Q())
|
||||
.then(function () {
|
||||
}, Q()).then(function () {
|
||||
// When chain is completed, return requirements array to upstream API
|
||||
return requirements;
|
||||
});
|
||||
|
@ -19,8 +19,8 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var Q = require('q'),
|
||||
build = require('./build');
|
||||
var Q = require('q');
|
||||
var build = require('./build');
|
||||
var path = require('path');
|
||||
var Adb = require('./Adb');
|
||||
var AndroidManifest = require('./AndroidManifest');
|
||||
@ -32,18 +32,16 @@ var events = require('cordova-common').events;
|
||||
* Returns a promise for the list of the device ID's found
|
||||
* @param lookHarder When true, try restarting adb if no devices are found.
|
||||
*/
|
||||
module.exports.list = function(lookHarder) {
|
||||
return Adb.devices()
|
||||
.then(function(list) {
|
||||
module.exports.list = function (lookHarder) {
|
||||
return Adb.devices().then(function (list) {
|
||||
if (list.length === 0 && lookHarder) {
|
||||
// adb kill-server doesn't seem to do the trick.
|
||||
// Could probably find a x-platform version of killall, but I'm not actually
|
||||
// sure that this scenario even happens on non-OSX machines.
|
||||
return spawn('killall', ['adb'])
|
||||
.then(function() {
|
||||
return spawn('killall', ['adb']).then(function () {
|
||||
events.emit('verbose', 'Restarting adb to see if more devices are detected.');
|
||||
return Adb.devices();
|
||||
}, function() {
|
||||
}, function () {
|
||||
// For non-killall OS's.
|
||||
return list;
|
||||
});
|
||||
@ -52,9 +50,8 @@ module.exports.list = function(lookHarder) {
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.resolveTarget = function(target) {
|
||||
return this.list(true)
|
||||
.then(function(device_list) {
|
||||
module.exports.resolveTarget = function (target) {
|
||||
return this.list(true).then(function (device_list) {
|
||||
if (!device_list || !device_list.length) {
|
||||
return Q.reject(new CordovaError('Failed to deploy to device, no devices found.'));
|
||||
}
|
||||
@ -65,8 +62,7 @@ module.exports.resolveTarget = function(target) {
|
||||
return Q.reject('ERROR: Unable to find target \'' + target + '\'.');
|
||||
}
|
||||
|
||||
return build.detectArchitecture(target)
|
||||
.then(function(arch) {
|
||||
return build.detectArchitecture(target).then(function (arch) {
|
||||
return { target: target, arch: arch, isEmulator: false };
|
||||
});
|
||||
});
|
||||
@ -77,43 +73,39 @@ module.exports.resolveTarget = function(target) {
|
||||
* and launches it.
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.install = function(target, buildResults) {
|
||||
return Q().then(function() {
|
||||
if (target && typeof target == 'object') {
|
||||
module.exports.install = function (target, buildResults) {
|
||||
return Q().then(function () {
|
||||
if (target && typeof target === 'object') {
|
||||
return target;
|
||||
}
|
||||
return module.exports.resolveTarget(target);
|
||||
}).then(function(resolvedTarget) {
|
||||
}).then(function (resolvedTarget) {
|
||||
var apk_path = build.findBestApkForArchitecture(buildResults, resolvedTarget.arch);
|
||||
var manifest = new AndroidManifest(path.join(__dirname, '../../AndroidManifest.xml'));
|
||||
var manifest = new AndroidManifest(path.join(__dirname, '../../app/src/main/AndroidManifest.xml'));
|
||||
var pkgName = manifest.getPackageId();
|
||||
var launchName = pkgName + '/.' + manifest.getActivity().getName();
|
||||
events.emit('log', 'Using apk: ' + apk_path);
|
||||
events.emit('log', 'Package name: ' + pkgName);
|
||||
|
||||
return Adb.install(resolvedTarget.target, apk_path, {replace: true})
|
||||
.catch(function (error) {
|
||||
return Adb.install(resolvedTarget.target, apk_path, {replace: true}).catch(function (error) {
|
||||
// CB-9557 CB-10157 only uninstall and reinstall app if the one that
|
||||
// is already installed on device was signed w/different certificate
|
||||
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString()))
|
||||
throw error;
|
||||
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; }
|
||||
|
||||
events.emit('warn', 'Uninstalling app from device and reinstalling it again because the ' +
|
||||
'installed app already signed with different key');
|
||||
|
||||
// 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.
|
||||
return Adb.uninstall(resolvedTarget.target, pkgName)
|
||||
.then(function() {
|
||||
return Adb.uninstall(resolvedTarget.target, pkgName).then(function () {
|
||||
return Adb.install(resolvedTarget.target, apk_path, {replace: true});
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
//unlock screen
|
||||
}).then(function () {
|
||||
// unlock screen
|
||||
return Adb.shell(resolvedTarget.target, 'input keyevent 82');
|
||||
}).then(function() {
|
||||
}).then(function () {
|
||||
return Adb.start(resolvedTarget.target, launchName);
|
||||
}).then(function() {
|
||||
}).then(function () {
|
||||
events.emit('log', 'LAUNCH SUCCESS');
|
||||
});
|
||||
});
|
||||
|
@ -21,8 +21,9 @@
|
||||
|
||||
/* jshint sub:true */
|
||||
|
||||
var retry = require('./retry');
|
||||
var build = require('./build');
|
||||
var android_versions = require('android-versions');
|
||||
var retry = require('./retry');
|
||||
var build = require('./build');
|
||||
var path = require('path');
|
||||
var Adb = require('./Adb');
|
||||
var AndroidManifest = require('./AndroidManifest');
|
||||
@ -33,20 +34,20 @@ var shelljs = require('shelljs');
|
||||
var android_sdk = require('./android_sdk');
|
||||
var check_reqs = require('./check_reqs');
|
||||
|
||||
var Q = require('q');
|
||||
var os = require('os');
|
||||
var fs = require('fs');
|
||||
var Q = require('q');
|
||||
var os = require('os');
|
||||
var fs = require('fs');
|
||||
var child_process = require('child_process');
|
||||
|
||||
// constants
|
||||
var ONE_SECOND = 1000; // in milliseconds
|
||||
var ONE_MINUTE = 60 * ONE_SECOND; // in milliseconds
|
||||
var ONE_SECOND = 1000; // in milliseconds
|
||||
var ONE_MINUTE = 60 * ONE_SECOND; // in milliseconds
|
||||
var INSTALL_COMMAND_TIMEOUT = 5 * ONE_MINUTE; // in milliseconds
|
||||
var NUM_INSTALL_RETRIES = 3;
|
||||
var CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds
|
||||
var EXEC_KILL_SIGNAL = 'SIGKILL';
|
||||
var NUM_INSTALL_RETRIES = 3;
|
||||
var CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds
|
||||
var EXEC_KILL_SIGNAL = 'SIGKILL';
|
||||
|
||||
function forgivingWhichSync(cmd) {
|
||||
function forgivingWhichSync (cmd) {
|
||||
try {
|
||||
return fs.realpathSync(shelljs.which(cmd));
|
||||
} catch (e) {
|
||||
@ -55,8 +56,7 @@ function forgivingWhichSync(cmd) {
|
||||
}
|
||||
|
||||
module.exports.list_images_using_avdmanager = function () {
|
||||
return superspawn.spawn('avdmanager', ['list', 'avd'])
|
||||
.then(function(output) {
|
||||
return superspawn.spawn('avdmanager', ['list', 'avd']).then(function (output) {
|
||||
var response = output.split('\n');
|
||||
var emulator_list = [];
|
||||
for (var i = 1; i < response.length; i++) {
|
||||
@ -108,16 +108,15 @@ module.exports.list_images_using_avdmanager = function () {
|
||||
/* To just return a list of names use this
|
||||
if (response[i].match(/Name:\s/)) {
|
||||
emulator_list.push(response[i].split('Name: ')[1].replace('\r', '');
|
||||
}*/
|
||||
} */
|
||||
|
||||
}
|
||||
return emulator_list;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.list_images_using_android = function() {
|
||||
return superspawn.spawn('android', ['list', 'avd'])
|
||||
.then(function(output) {
|
||||
module.exports.list_images_using_android = function () {
|
||||
return superspawn.spawn('android', ['list', 'avd']).then(function (output) {
|
||||
var response = output.split('\n');
|
||||
var emulator_list = [];
|
||||
for (var i = 1; i < response.length; i++) {
|
||||
@ -152,7 +151,7 @@ module.exports.list_images_using_android = function() {
|
||||
/* To just return a list of names use this
|
||||
if (response[i].match(/Name:\s/)) {
|
||||
emulator_list.push(response[i].split('Name: ')[1].replace('\r', '');
|
||||
}*/
|
||||
} */
|
||||
|
||||
}
|
||||
return emulator_list;
|
||||
@ -170,16 +169,30 @@ module.exports.list_images_using_android = function() {
|
||||
skin : <skin>
|
||||
}
|
||||
*/
|
||||
module.exports.list_images = function() {
|
||||
if (forgivingWhichSync('avdmanager')) {
|
||||
return module.exports.list_images_using_avdmanager();
|
||||
} else if (forgivingWhichSync('android')) {
|
||||
return module.exports.list_images_using_android();
|
||||
} else {
|
||||
return Q().then(function() {
|
||||
throw new CordovaError('Could not find either `android` or `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?');
|
||||
module.exports.list_images = function () {
|
||||
return Q.fcall(function () {
|
||||
if (forgivingWhichSync('avdmanager')) {
|
||||
return module.exports.list_images_using_avdmanager();
|
||||
} else if (forgivingWhichSync('android')) {
|
||||
return module.exports.list_images_using_android();
|
||||
} else {
|
||||
return Q().then(function () {
|
||||
throw new CordovaError('Could not find either `android` or `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?');
|
||||
});
|
||||
}
|
||||
}).then(function (avds) {
|
||||
// In case we're missing the Android OS version string from the target description, add it.
|
||||
return avds.map(function (avd) {
|
||||
if (avd.target && avd.target.indexOf('Android API') > -1 && avd.target.indexOf('API level') < 0) {
|
||||
var api_level = avd.target.match(/\d+/);
|
||||
if (api_level) {
|
||||
var level = android_versions.get(api_level);
|
||||
avd.target = 'Android ' + level.semver + ' (API level ' + api_level + ')';
|
||||
}
|
||||
}
|
||||
return avd;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -187,20 +200,19 @@ module.exports.list_images = function() {
|
||||
* or undefined if no avds exist.
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.best_image = function() {
|
||||
return this.list_images()
|
||||
.then(function(images) {
|
||||
module.exports.best_image = function () {
|
||||
return this.list_images().then(function (images) {
|
||||
// Just return undefined if there is no images
|
||||
if (images.length === 0) return;
|
||||
|
||||
var closest = 9999;
|
||||
var best = images[0];
|
||||
var project_target = check_reqs.get_target().replace('android-', '');
|
||||
var project_target = parseInt(check_reqs.get_target().replace('android-', ''));
|
||||
for (var i in images) {
|
||||
var target = images[i].target;
|
||||
if(target) {
|
||||
var num = target.split('(API level ')[1].replace(')', '');
|
||||
if (num == project_target) {
|
||||
if (target && target.indexOf('API level') > -1) {
|
||||
var num = parseInt(target.split('(API level ')[1].replace(')', ''));
|
||||
if (num === project_target) {
|
||||
return images[i];
|
||||
} else if (project_target - num < closest && project_target > num) {
|
||||
closest = project_target - num;
|
||||
@ -213,19 +225,18 @@ module.exports.best_image = function() {
|
||||
};
|
||||
|
||||
// Returns a promise.
|
||||
module.exports.list_started = function() {
|
||||
module.exports.list_started = function () {
|
||||
return Adb.devices({emulators: true});
|
||||
};
|
||||
|
||||
// Returns a promise.
|
||||
// TODO: we should remove this, there's a more robust method under android_sdk.js
|
||||
module.exports.list_targets = function() {
|
||||
return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()})
|
||||
.then(function(output) {
|
||||
module.exports.list_targets = function () {
|
||||
return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()}).then(function (output) {
|
||||
var target_out = output.split('\n');
|
||||
var targets = [];
|
||||
for (var i = target_out.length; i >= 0; i--) {
|
||||
if(target_out[i].match(/id:/)) {
|
||||
if (target_out[i].match(/id:/)) {
|
||||
targets.push(targets[i].split(' ')[1]);
|
||||
}
|
||||
}
|
||||
@ -240,9 +251,8 @@ module.exports.list_targets = function() {
|
||||
module.exports.get_available_port = function () {
|
||||
var self = this;
|
||||
|
||||
return self.list_started()
|
||||
.then(function (emulators) {
|
||||
for (var p = 5584; p >= 5554; p-=2) {
|
||||
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;
|
||||
@ -262,14 +272,13 @@ module.exports.get_available_port = function () {
|
||||
*
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.start = function(emulator_ID, boot_timeout) {
|
||||
module.exports.start = function (emulator_ID, boot_timeout) {
|
||||
var self = this;
|
||||
|
||||
return Q().then(function() {
|
||||
return Q().then(function () {
|
||||
if (emulator_ID) return Q(emulator_ID);
|
||||
|
||||
return self.best_image()
|
||||
.then(function(best) {
|
||||
return self.best_image().then(function (best) {
|
||||
if (best && best.name) {
|
||||
events.emit('warn', 'No emulator specified, defaulting to ' + best.name);
|
||||
return best.name;
|
||||
@ -281,9 +290,8 @@ module.exports.start = function(emulator_ID, boot_timeout) {
|
||||
'2. Create an AVD by running: ' + androidCmd + ' avd\n' +
|
||||
'HINT: For a faster emulator, use an Intel System Image and install the HAXM device driver\n'));
|
||||
});
|
||||
}).then(function(emulatorId) {
|
||||
return self.get_available_port()
|
||||
.then(function (port) {
|
||||
}).then(function (emulatorId) {
|
||||
return self.get_available_port().then(function (port) {
|
||||
// Figure out the directory the emulator binary runs in, and set the cwd to that directory.
|
||||
// Workaround for https://code.google.com/p/android/issues/detail?id=235461
|
||||
var emulator_dir = path.dirname(shelljs.which('emulator'));
|
||||
@ -297,20 +305,17 @@ module.exports.start = function(emulator_ID, boot_timeout) {
|
||||
events.emit('log', 'Waiting for emulator to start...');
|
||||
return self.wait_for_emulator(port);
|
||||
});
|
||||
}).then(function(emulatorId) {
|
||||
if (!emulatorId)
|
||||
return Q.reject(new CordovaError('Failed to start emulator'));
|
||||
}).then(function (emulatorId) {
|
||||
if (!emulatorId) { return Q.reject(new CordovaError('Failed to start emulator')); }
|
||||
|
||||
//wait for emulator to boot up
|
||||
// wait for emulator to boot up
|
||||
process.stdout.write('Waiting for emulator to boot (this may take a while)...');
|
||||
return self.wait_for_boot(emulatorId, boot_timeout)
|
||||
.then(function(success) {
|
||||
return self.wait_for_boot(emulatorId, boot_timeout).then(function (success) {
|
||||
if (success) {
|
||||
events.emit('log','BOOT COMPLETE');
|
||||
//unlock screen
|
||||
return Adb.shell(emulatorId, 'input keyevent 82')
|
||||
.then(function() {
|
||||
//return the new emulator id for the started emulators
|
||||
events.emit('log', 'BOOT COMPLETE');
|
||||
// unlock screen
|
||||
return Adb.shell(emulatorId, 'input keyevent 82').then(function () {
|
||||
// return the new emulator id for the started emulators
|
||||
return emulatorId;
|
||||
});
|
||||
} else {
|
||||
@ -325,20 +330,19 @@ module.exports.start = function(emulator_ID, boot_timeout) {
|
||||
* Waits for an emulator to boot on a given port.
|
||||
* Returns this emulator's ID in a promise.
|
||||
*/
|
||||
module.exports.wait_for_emulator = function(port) {
|
||||
module.exports.wait_for_emulator = function (port) {
|
||||
var self = this;
|
||||
return Q().then(function() {
|
||||
return Q().then(function () {
|
||||
var emulator_id = 'emulator-' + port;
|
||||
return Adb.shell(emulator_id, 'getprop dev.bootcomplete')
|
||||
.then(function (output) {
|
||||
return Adb.shell(emulator_id, 'getprop dev.bootcomplete').then(function (output) {
|
||||
if (output.indexOf('1') >= 0) {
|
||||
return emulator_id;
|
||||
}
|
||||
return self.wait_for_emulator(port);
|
||||
}, function (error) {
|
||||
if (error && error.message &&
|
||||
(error.message.indexOf('not found') > -1) ||
|
||||
error.message.indexOf('device offline') > -1) {
|
||||
if ((error && error.message &&
|
||||
(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 {
|
||||
@ -346,7 +350,7 @@ module.exports.wait_for_emulator = function(port) {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
@ -354,10 +358,9 @@ module.exports.wait_for_emulator = function(port) {
|
||||
* promise that resolves to a boolean indicating success. Not specifying a
|
||||
* time_remaining or passing a negative value will cause it to wait forever
|
||||
*/
|
||||
module.exports.wait_for_boot = function(emulator_id, time_remaining) {
|
||||
module.exports.wait_for_boot = function (emulator_id, time_remaining) {
|
||||
var self = this;
|
||||
return Adb.shell(emulator_id, 'ps')
|
||||
.then(function(output) {
|
||||
return Adb.shell(emulator_id, 'ps').then(function (output) {
|
||||
if (output.match(/android\.process\.acore/)) {
|
||||
return true;
|
||||
} else if (time_remaining === 0) {
|
||||
@ -366,7 +369,7 @@ module.exports.wait_for_boot = function(emulator_id, time_remaining) {
|
||||
process.stdout.write('.');
|
||||
|
||||
// Check at regular intervals
|
||||
return Q.delay(time_remaining < CHECK_BOOTED_INTERVAL ? time_remaining : CHECK_BOOTED_INTERVAL).then(function() {
|
||||
return Q.delay(time_remaining < CHECK_BOOTED_INTERVAL ? time_remaining : CHECK_BOOTED_INTERVAL).then(function () {
|
||||
var updated_time = time_remaining >= 0 ? Math.max(time_remaining - CHECK_BOOTED_INTERVAL, 0) : time_remaining;
|
||||
return self.wait_for_boot(emulator_id, updated_time);
|
||||
});
|
||||
@ -379,11 +382,10 @@ module.exports.wait_for_boot = function(emulator_id, time_remaining) {
|
||||
* TODO : Enter the stdin input required to complete the creation of an avd.
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.create_image = function(name, target) {
|
||||
module.exports.create_image = function (name, target) {
|
||||
console.log('Creating new avd named ' + name);
|
||||
if (target) {
|
||||
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', target])
|
||||
.then(null, function(error) {
|
||||
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', target]).then(null, function (error) {
|
||||
console.error('ERROR : Failed to create emulator image : ');
|
||||
console.error(' Do you have the latest android targets including ' + target + '?');
|
||||
console.error(error);
|
||||
@ -391,22 +393,20 @@ module.exports.create_image = function(name, target) {
|
||||
} else {
|
||||
console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.');
|
||||
// TODO: there's a more robust method for finding targets in android_sdk.js
|
||||
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]])
|
||||
.then(function() {
|
||||
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]]).then(function () {
|
||||
// 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('Ensure you have targets available by running the "android" command');
|
||||
return Q.reject();
|
||||
}, function(error) {
|
||||
}, function (error) {
|
||||
console.error('ERROR : Failed to create emulator image : ');
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.resolveTarget = function(target) {
|
||||
return this.list_started()
|
||||
.then(function(emulator_list) {
|
||||
module.exports.resolveTarget = function (target) {
|
||||
return this.list_started().then(function (emulator_list) {
|
||||
if (emulator_list.length < 1) {
|
||||
return Q.reject('No running Android emulators found, please start an emulator before deploying your project.');
|
||||
}
|
||||
@ -417,9 +417,8 @@ module.exports.resolveTarget = function(target) {
|
||||
return Q.reject('Unable to find target \'' + target + '\'. Failed to deploy to emulator.');
|
||||
}
|
||||
|
||||
return build.detectArchitecture(target)
|
||||
.then(function(arch) {
|
||||
return {target:target, arch:arch, isEmulator:true};
|
||||
return build.detectArchitecture(target).then(function (arch) {
|
||||
return {target: target, arch: arch, isEmulator: true};
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -430,15 +429,20 @@ module.exports.resolveTarget = function(target) {
|
||||
* If no started emulators are found, error out.
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.install = function(givenTarget, buildResults) {
|
||||
module.exports.install = function (givenTarget, buildResults) {
|
||||
|
||||
var target;
|
||||
var manifest = new AndroidManifest(path.join(__dirname, '../../AndroidManifest.xml'));
|
||||
// We need to find the proper path to the Android Manifest
|
||||
var manifestPath = path.join(__dirname, '..', '..', 'app', 'src', 'main', 'AndroidManifest.xml');
|
||||
if (buildResults.buildMethod === 'gradle') {
|
||||
manifestPath = path.join(__dirname, '../../AndroidManifest.xml');
|
||||
}
|
||||
var manifest = new AndroidManifest(manifestPath);
|
||||
var pkgName = manifest.getPackageId();
|
||||
|
||||
// resolve the target emulator
|
||||
return Q().then(function () {
|
||||
if (givenTarget && typeof givenTarget == 'object') {
|
||||
if (givenTarget && typeof givenTarget === 'object') {
|
||||
return givenTarget;
|
||||
} else {
|
||||
return module.exports.resolveTarget(givenTarget);
|
||||
@ -452,13 +456,12 @@ module.exports.install = function(givenTarget, buildResults) {
|
||||
}).then(function () {
|
||||
// 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.
|
||||
return Q.when()
|
||||
.then(function() {
|
||||
return Q.when().then(function () {
|
||||
|
||||
var apk_path = build.findBestApkForArchitecture(buildResults, target.arch);
|
||||
var execOptions = {
|
||||
cwd: os.tmpdir(),
|
||||
timeout: INSTALL_COMMAND_TIMEOUT, // in milliseconds
|
||||
timeout: INSTALL_COMMAND_TIMEOUT, // in milliseconds
|
||||
killSignal: EXEC_KILL_SIGNAL
|
||||
};
|
||||
|
||||
@ -469,12 +472,12 @@ module.exports.install = function(givenTarget, buildResults) {
|
||||
// A special function to call adb install in specific environment w/ specific options.
|
||||
// Introduced as a part of fix for http://issues.apache.org/jira/browse/CB-9119
|
||||
// to workaround sporadic emulator hangs
|
||||
function adbInstallWithOptions(target, apk, opts) {
|
||||
function adbInstallWithOptions (target, apk, opts) {
|
||||
events.emit('verbose', 'Installing apk ' + apk + ' on ' + target + '...');
|
||||
|
||||
var command = 'adb -s ' + target + ' install -r "' + apk + '"';
|
||||
return Q.promise(function (resolve, reject) {
|
||||
child_process.exec(command, opts, function(err, stdout, stderr) {
|
||||
child_process.exec(command, opts, function (err, stdout, 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
|
||||
// message to stdout, so we have to use RegExp matching to detect installation failure.
|
||||
@ -494,27 +497,23 @@ module.exports.install = function(givenTarget, buildResults) {
|
||||
}
|
||||
|
||||
function installPromise () {
|
||||
return adbInstallWithOptions(target.target, apk_path, execOptions)
|
||||
.catch(function (error) {
|
||||
return adbInstallWithOptions(target.target, apk_path, execOptions).catch(function (error) {
|
||||
// CB-9557 CB-10157 only uninstall and reinstall app if the one that
|
||||
// is already installed on device was signed w/different certificate
|
||||
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString()))
|
||||
throw error;
|
||||
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; }
|
||||
|
||||
events.emit('warn', 'Uninstalling app from device and reinstalling it because the ' +
|
||||
'currently installed app was signed with different key');
|
||||
|
||||
// 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.
|
||||
return Adb.uninstall(target.target, pkgName)
|
||||
.then(function() {
|
||||
return Adb.uninstall(target.target, pkgName).then(function () {
|
||||
return adbInstallWithOptions(target.target, apk_path, execOptions);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return retry.retryPromise(NUM_INSTALL_RETRIES, installPromise)
|
||||
.then(function (output) {
|
||||
return retry.retryPromise(NUM_INSTALL_RETRIES, installPromise).then(function (output) {
|
||||
events.emit('log', 'INSTALL SUCCESS');
|
||||
});
|
||||
});
|
||||
|
@ -0,0 +1,3 @@
|
||||
@ECHO OFF
|
||||
for /f "tokens=2*" %%a in ('REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Android Studio" /v Path') do set "ASPath=%%~b"
|
||||
ECHO %ASPath%
|
@ -18,7 +18,7 @@
|
||||
@ECHO OFF
|
||||
SET script_path="%~dp0install-device"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
node %script_path% %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'install-device' script in 'cordova\lib' folder, aborting...>&2
|
||||
|
@ -18,7 +18,7 @@
|
||||
@ECHO OFF
|
||||
SET script_path="%~dp0install-emulator"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
node %script_path% %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'install-emulator' script in 'cordova\lib' folder, aborting...>&2
|
||||
|
@ -18,7 +18,7 @@
|
||||
@ECHO OFF
|
||||
SET script_path="%~dp0list-devices"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
node %script_path% %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'list-devices' script in 'cordova\lib' folder, aborting...>&2
|
||||
|
@ -18,7 +18,7 @@
|
||||
@ECHO OFF
|
||||
SET script_path="%~dp0list-emulator-images"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
node %script_path% %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'list-emulator-images' script in 'cordova\lib' folder, aborting...>&2
|
||||
|
@ -18,7 +18,7 @@
|
||||
@ECHO OFF
|
||||
SET script_path="%~dp0list-started-emulators"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
node %script_path% %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'list-started-emulators' script in 'cordova\lib' folder, aborting...>&2
|
||||
|
@ -19,28 +19,28 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var path = require('path'),
|
||||
os = require('os'),
|
||||
Q = require('q'),
|
||||
child_process = require('child_process'),
|
||||
ROOT = path.join(__dirname, '..', '..');
|
||||
var path = require('path');
|
||||
var os = require('os');
|
||||
var Q = require('q');
|
||||
var child_process = require('child_process');
|
||||
var ROOT = path.join(__dirname, '..', '..');
|
||||
|
||||
/*
|
||||
* Starts running logcat in the shell.
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.run = function() {
|
||||
module.exports.run = function () {
|
||||
var d = Q.defer();
|
||||
var adb = child_process.spawn('adb', ['logcat'], {cwd: os.tmpdir()});
|
||||
|
||||
adb.stdout.on('data', function(data) {
|
||||
adb.stdout.on('data', function (data) {
|
||||
var lines = data ? data.toString().split('\n') : [];
|
||||
var out = lines.filter(function(x) { return x.indexOf('nativeGetEnabledTags') < 0; });
|
||||
var out = lines.filter(function (x) { return x.indexOf('nativeGetEnabledTags') < 0; });
|
||||
console.log(out.join('\n'));
|
||||
});
|
||||
|
||||
adb.stderr.on('data', console.error);
|
||||
adb.on('close', function(code) {
|
||||
adb.on('close', function (code) {
|
||||
if (code > 0) {
|
||||
d.reject('Failed to run logcat command.');
|
||||
} else d.resolve();
|
||||
@ -49,7 +49,7 @@ module.exports.run = function() {
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
module.exports.help = function() {
|
||||
module.exports.help = function () {
|
||||
console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'log')));
|
||||
console.log('Gives the logcat output on the command line.');
|
||||
process.exit(0);
|
||||
|
@ -20,8 +20,10 @@
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
maven {
|
||||
url "https://maven.google.com"
|
||||
}
|
||||
}
|
||||
|
||||
// Switch the Android Gradle plugin version requirement depending on the
|
||||
|
@ -1,7 +1,4 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2013 Anis Kadri
|
||||
*
|
||||
* 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
|
||||
@ -26,15 +23,26 @@ var events = require('cordova-common').events;
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
|
||||
var handlers = {
|
||||
'source-file':{
|
||||
install:function(obj, plugin, project, options) {
|
||||
'source-file': {
|
||||
install: function (obj, plugin, project, options) {
|
||||
if (!obj.src) throw new CordovaError(generateAttributeError('src', 'source-file', plugin.id));
|
||||
if (!obj.targetDir) throw new CordovaError(generateAttributeError('target-dir', 'source-file', plugin.id));
|
||||
|
||||
var dest = path.join(obj.targetDir, path.basename(obj.src));
|
||||
|
||||
if(options && options.android_studio === true) {
|
||||
dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
|
||||
// TODO: This code needs to be replaced, since the core plugins need to be re-mapped to a different location in
|
||||
// a later plugins release. This is for legacy plugins to work with Cordova.
|
||||
|
||||
if (options && options.android_studio === true) {
|
||||
// If a Java file is using the new directory structure, don't penalize it
|
||||
if (!obj.targetDir.includes('app/src/main')) {
|
||||
if (obj.src.endsWith('.java')) {
|
||||
dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
|
||||
} else if (obj.src.endsWith('.xml')) {
|
||||
// We are making a huge assumption here that XML files will be going to res/xml or values/xml
|
||||
dest = path.join('app/src/main', obj.targetDir, path.basename(obj.src));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options && options.force) {
|
||||
@ -43,42 +51,50 @@ var handlers = {
|
||||
copyNewFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
|
||||
}
|
||||
},
|
||||
uninstall:function(obj, plugin, project, options) {
|
||||
uninstall: function (obj, plugin, project, options) {
|
||||
var dest = path.join(obj.targetDir, path.basename(obj.src));
|
||||
|
||||
if(options && options.android_studio === true) {
|
||||
dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
|
||||
|
||||
if (options && options.android_studio === true) {
|
||||
dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
|
||||
}
|
||||
|
||||
deleteJava(project.projectDir, dest);
|
||||
}
|
||||
},
|
||||
'lib-file':{
|
||||
install:function(obj, plugin, project, options) {
|
||||
'lib-file': {
|
||||
install: function (obj, plugin, project, options) {
|
||||
var dest = path.join('libs', path.basename(obj.src));
|
||||
if(options && options.android_studio === true) {
|
||||
dest = path.join('app/libs', path.basename(obj.src));
|
||||
if (options && options.android_studio === true) {
|
||||
dest = path.join('app/libs', path.basename(obj.src));
|
||||
}
|
||||
copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
|
||||
},
|
||||
uninstall:function(obj, plugin, project, options) {
|
||||
uninstall: function (obj, plugin, project, options) {
|
||||
var dest = path.join('libs', path.basename(obj.src));
|
||||
if(options && options.android_studio === true) {
|
||||
dest = path.join('app/libs', path.basename(obj.src));
|
||||
if (options && options.android_studio === true) {
|
||||
dest = path.join('app/libs', path.basename(obj.src));
|
||||
}
|
||||
removeFile(project.projectDir, dest);
|
||||
}
|
||||
},
|
||||
'resource-file':{
|
||||
install:function(obj, plugin, project, options) {
|
||||
copyFile(plugin.dir, obj.src, project.projectDir, path.normalize(obj.target), !!(options && options.link));
|
||||
'resource-file': {
|
||||
install: function (obj, plugin, project, options) {
|
||||
var dest = path.normalize(obj.target);
|
||||
if (options && options.android_studio === true) {
|
||||
dest = path.join('app/src/main', dest);
|
||||
}
|
||||
copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
|
||||
},
|
||||
uninstall:function(obj, plugin, project, options) {
|
||||
removeFile(project.projectDir, path.normalize(obj.target));
|
||||
uninstall: function (obj, plugin, project, options) {
|
||||
var dest = path.normalize(obj.target);
|
||||
if (options && options.android_studio === true) {
|
||||
dest = path.join('app/src/main', dest);
|
||||
}
|
||||
removeFile(project.projectDir, dest);
|
||||
}
|
||||
},
|
||||
'framework': {
|
||||
install:function(obj, plugin, project, options) {
|
||||
install: function (obj, plugin, project, options) {
|
||||
var src = obj.src;
|
||||
if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
|
||||
|
||||
@ -95,15 +111,15 @@ var handlers = {
|
||||
subDir = src;
|
||||
}
|
||||
|
||||
if (obj.type == 'gradleReference') {
|
||||
if (obj.type === 'gradleReference') {
|
||||
project.addGradleReference(parentDir, subDir);
|
||||
} else if (obj.type == 'sys') {
|
||||
} else if (obj.type === 'sys') {
|
||||
project.addSystemLibrary(parentDir, subDir);
|
||||
} else {
|
||||
project.addSubProject(parentDir, subDir);
|
||||
}
|
||||
},
|
||||
uninstall:function(obj, plugin, project, options) {
|
||||
uninstall: function (obj, plugin, project, options) {
|
||||
var src = obj.src;
|
||||
if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
|
||||
|
||||
@ -125,17 +141,17 @@ var handlers = {
|
||||
subDir = src;
|
||||
}
|
||||
|
||||
if (obj.type == 'gradleReference') {
|
||||
if (obj.type === 'gradleReference') {
|
||||
project.removeGradleReference(parentDir, subDir);
|
||||
} else if (obj.type == 'sys') {
|
||||
} else if (obj.type === 'sys') {
|
||||
project.removeSystemLibrary(parentDir, subDir);
|
||||
} else {
|
||||
project.removeSubProject(parentDir, subDir);
|
||||
}
|
||||
}
|
||||
},
|
||||
asset:{
|
||||
install:function(obj, plugin, project, options) {
|
||||
asset: {
|
||||
install: function (obj, plugin, project, options) {
|
||||
if (!obj.src) {
|
||||
throw new CordovaError(generateAttributeError('src', 'asset', plugin.id));
|
||||
}
|
||||
@ -149,7 +165,7 @@ var handlers = {
|
||||
copyFile(plugin.dir, obj.src, project.platformWww, obj.target);
|
||||
}
|
||||
},
|
||||
uninstall:function(obj, plugin, project, options) {
|
||||
uninstall: function (obj, plugin, project, options) {
|
||||
var target = obj.target || obj.src;
|
||||
|
||||
if (!target) throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
|
||||
@ -167,7 +183,7 @@ var handlers = {
|
||||
install: function (obj, plugin, project, options) {
|
||||
// Copy the plugin's files into the www directory.
|
||||
var moduleSource = path.resolve(plugin.dir, obj.src);
|
||||
var moduleName = plugin.id + '.' + (obj.name || path.basename(obj.src, path.extname (obj.src)));
|
||||
var moduleName = plugin.id + '.' + (obj.name || path.basename(obj.src, path.extname(obj.src)));
|
||||
|
||||
// Read in the file, prepend the cordova.define, and write it back out.
|
||||
var scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM
|
||||
@ -206,7 +222,7 @@ module.exports.getInstaller = function (type) {
|
||||
events.emit('verbose', '<' + type + '> is not supported for android plugins');
|
||||
};
|
||||
|
||||
module.exports.getUninstaller = function(type) {
|
||||
module.exports.getUninstaller = function (type) {
|
||||
if (handlers[type] && handlers[type].uninstall) {
|
||||
return handlers[type].uninstall;
|
||||
}
|
||||
@ -221,21 +237,19 @@ function copyFile (plugin_dir, src, project_dir, dest, link) {
|
||||
// check that src path is inside plugin directory
|
||||
var real_path = fs.realpathSync(src);
|
||||
var real_plugin_path = fs.realpathSync(plugin_dir);
|
||||
if (real_path.indexOf(real_plugin_path) !== 0)
|
||||
throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"');
|
||||
if (real_path.indexOf(real_plugin_path) !== 0) { throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"'); }
|
||||
|
||||
dest = path.resolve(project_dir, dest);
|
||||
|
||||
// check that dest path is located in project directory
|
||||
if (dest.indexOf(project_dir) !== 0)
|
||||
throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project');
|
||||
if (dest.indexOf(project_dir) !== 0) { throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project'); }
|
||||
|
||||
shell.mkdir('-p', path.dirname(dest));
|
||||
if (link) {
|
||||
symlinkFileOrDirTree(src, dest);
|
||||
} else if (fs.statSync(src).isDirectory()) {
|
||||
// XXX shelljs decides to create a directory when -R|-r is used which sucks. http://goo.gl/nbsjq
|
||||
shell.cp('-Rf', src+'/*', dest);
|
||||
shell.cp('-Rf', src + '/*', dest);
|
||||
} else {
|
||||
shell.cp('-f', src, dest);
|
||||
}
|
||||
@ -244,24 +258,22 @@ function copyFile (plugin_dir, src, project_dir, dest, link) {
|
||||
// Same as copy file but throws error if target exists
|
||||
function copyNewFile (plugin_dir, src, project_dir, dest, link) {
|
||||
var target_path = path.resolve(project_dir, dest);
|
||||
if (fs.existsSync(target_path))
|
||||
throw new CordovaError('"' + target_path + '" already exists!');
|
||||
if (fs.existsSync(target_path)) { throw new CordovaError('"' + target_path + '" already exists!'); }
|
||||
|
||||
copyFile(plugin_dir, src, project_dir, dest, !!link);
|
||||
}
|
||||
|
||||
function symlinkFileOrDirTree(src, dest) {
|
||||
function symlinkFileOrDirTree (src, dest) {
|
||||
if (fs.existsSync(dest)) {
|
||||
shell.rm('-Rf', dest);
|
||||
}
|
||||
|
||||
if (fs.statSync(src).isDirectory()) {
|
||||
shell.mkdir('-p', dest);
|
||||
fs.readdirSync(src).forEach(function(entry) {
|
||||
fs.readdirSync(src).forEach(function (entry) {
|
||||
symlinkFileOrDirTree(path.join(src, entry), path.join(dest, entry));
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
fs.symlinkSync(path.relative(fs.realpathSync(path.dirname(dest)), src), dest);
|
||||
}
|
||||
}
|
||||
@ -292,8 +304,8 @@ function removeFileAndParents (baseDir, destFile, stopper) {
|
||||
// check if directory is empty
|
||||
var curDir = path.dirname(file);
|
||||
|
||||
while(curDir !== path.resolve(baseDir, stopper)) {
|
||||
if(fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) {
|
||||
while (curDir !== path.resolve(baseDir, stopper)) {
|
||||
if (fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) {
|
||||
fs.rmdirSync(curDir);
|
||||
curDir = path.resolve(curDir, '..');
|
||||
} else {
|
||||
@ -303,6 +315,6 @@ function removeFileAndParents (baseDir, destFile, stopper) {
|
||||
}
|
||||
}
|
||||
|
||||
function generateAttributeError(attribute, element, id) {
|
||||
function generateAttributeError (attribute, element, id) {
|
||||
return 'Required attribute "' + attribute + '" not specified in <' + element + '> element from plugin: ' + id;
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
/* eslint no-useless-escape: 0 */
|
||||
|
||||
var Q = require('q');
|
||||
var fs = require('fs');
|
||||
@ -23,6 +24,7 @@ var path = require('path');
|
||||
var shell = require('shelljs');
|
||||
var events = require('cordova-common').events;
|
||||
var AndroidManifest = require('./AndroidManifest');
|
||||
var checkReqs = require('./check_reqs');
|
||||
var xmlHelpers = require('cordova-common').xmlHelpers;
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
var ConfigParser = require('cordova-common').ConfigParser;
|
||||
@ -40,17 +42,14 @@ module.exports.prepare = function (cordovaProject, options) {
|
||||
this._config = updateConfigFilesFrom(cordovaProject.projectConfig, munger, this.locations);
|
||||
|
||||
// Update own www dir with project's www assets and plugins' assets and js-files
|
||||
return Q.when(updateWww(cordovaProject, this.locations))
|
||||
.then(function () {
|
||||
return Q.when(updateWww(cordovaProject, this.locations)).then(function () {
|
||||
// update project according to config.xml changes.
|
||||
return updateProjectAccordingTo(self._config, self.locations);
|
||||
})
|
||||
.then(function () {
|
||||
}).then(function () {
|
||||
updateIcons(cordovaProject, path.relative(cordovaProject.root, self.locations.res));
|
||||
updateSplashes(cordovaProject, path.relative(cordovaProject.root, self.locations.res));
|
||||
updateFileResources(cordovaProject, path.relative(cordovaProject.root, self.locations.root));
|
||||
})
|
||||
.then(function () {
|
||||
}).then(function () {
|
||||
events.emit('verbose', 'Prepared android project successfully');
|
||||
});
|
||||
};
|
||||
@ -91,7 +90,7 @@ module.exports.clean = function (options) {
|
||||
* represents current project's configuration. When returned, the
|
||||
* configuration is already dumped to appropriate config.xml file.
|
||||
*/
|
||||
function updateConfigFilesFrom(sourceConfig, configMunger, locations) {
|
||||
function updateConfigFilesFrom (sourceConfig, configMunger, locations) {
|
||||
events.emit('verbose', 'Generating platform-specific config.xml from defaults for android at ' + locations.configXml);
|
||||
|
||||
// First cleanup current config and merge project's one into own
|
||||
@ -106,7 +105,7 @@ function updateConfigFilesFrom(sourceConfig, configMunger, locations) {
|
||||
// Merge changes from app's config.xml into platform's one
|
||||
var config = new ConfigParser(locations.configXml);
|
||||
xmlHelpers.mergeXml(sourceConfig.doc.getroot(),
|
||||
config.doc.getroot(), 'android', /*clobber=*/true);
|
||||
config.doc.getroot(), 'android', /* clobber= */true);
|
||||
|
||||
config.write();
|
||||
return config;
|
||||
@ -115,7 +114,7 @@ function updateConfigFilesFrom(sourceConfig, configMunger, locations) {
|
||||
/**
|
||||
* Logs all file operations via the verbose event stream, indented.
|
||||
*/
|
||||
function logFileOp(message) {
|
||||
function logFileOp (message) {
|
||||
events.emit('verbose', ' ' + message);
|
||||
}
|
||||
|
||||
@ -128,7 +127,7 @@ function logFileOp(message) {
|
||||
* @param {Object} destinations An object that contains destination
|
||||
* paths for www files.
|
||||
*/
|
||||
function updateWww(cordovaProject, destinations) {
|
||||
function updateWww (cordovaProject, destinations) {
|
||||
var sourceDirs = [
|
||||
path.relative(cordovaProject.root, cordovaProject.locations.www),
|
||||
path.relative(cordovaProject.root, destinations.platformWww)
|
||||
@ -151,7 +150,7 @@ function updateWww(cordovaProject, destinations) {
|
||||
/**
|
||||
* Cleans all files from the platform 'www' directory.
|
||||
*/
|
||||
function cleanWww(projectRoot, locations) {
|
||||
function cleanWww (projectRoot, locations) {
|
||||
var targetDir = path.relative(projectRoot, locations.www);
|
||||
events.emit('verbose', 'Cleaning ' + targetDir);
|
||||
|
||||
@ -167,19 +166,26 @@ function cleanWww(projectRoot, locations) {
|
||||
* be used to update project
|
||||
* @param {Object} locations A map of locations for this platform
|
||||
*/
|
||||
function updateProjectAccordingTo(platformConfig, locations) {
|
||||
function updateProjectAccordingTo (platformConfig, locations) {
|
||||
// Update app name by editing res/values/strings.xml
|
||||
var name = platformConfig.name();
|
||||
var strings = xmlHelpers.parseElementtreeSync(locations.strings);
|
||||
|
||||
var name = platformConfig.name();
|
||||
strings.find('string[@name="app_name"]').text = name.replace(/\'/g, '\\\'');
|
||||
|
||||
var shortName = platformConfig.shortName && platformConfig.shortName();
|
||||
if (shortName && shortName !== name) {
|
||||
strings.find('string[@name="launcher_name"]').text = shortName.replace(/\'/g, '\\\'');
|
||||
}
|
||||
|
||||
fs.writeFileSync(locations.strings, strings.write({indent: 4}), 'utf-8');
|
||||
events.emit('verbose', 'Wrote out android application name "' + name + '" to ' + locations.strings);
|
||||
|
||||
// Java packages cannot support dashes
|
||||
var pkg = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_');
|
||||
var androidPkgName = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_');
|
||||
|
||||
var manifest = new AndroidManifest(locations.manifest);
|
||||
var orig_pkg = manifest.getPackageId();
|
||||
var manifestId = manifest.getPackageId();
|
||||
|
||||
manifest.getActivity()
|
||||
.setOrientation(platformConfig.getPreference('orientation'))
|
||||
@ -187,36 +193,41 @@ function updateProjectAccordingTo(platformConfig, locations) {
|
||||
|
||||
manifest.setVersionName(platformConfig.version())
|
||||
.setVersionCode(platformConfig.android_versionCode() || default_versionCode(platformConfig.version()))
|
||||
.setPackageId(pkg)
|
||||
.setPackageId(androidPkgName)
|
||||
.setMinSdkVersion(platformConfig.getPreference('android-minSdkVersion', 'android'))
|
||||
.setMaxSdkVersion(platformConfig.getPreference('android-maxSdkVersion', 'android'))
|
||||
.setTargetSdkVersion(platformConfig.getPreference('android-targetSdkVersion', 'android'))
|
||||
.write();
|
||||
|
||||
var javaPattern = path.join(locations.root, 'src', orig_pkg.replace(/\./g, '/'), '*.java');
|
||||
var java_files = shell.ls(javaPattern).filter(function(f) {
|
||||
// Java file paths shouldn't be hard coded
|
||||
var javaPattern = path.join(locations.javaSrc, manifestId.replace(/\./g, '/'), '*.java');
|
||||
var java_files = shell.ls(javaPattern).filter(function (f) {
|
||||
return shell.grep(/extends\s+CordovaActivity/g, f);
|
||||
});
|
||||
|
||||
if (java_files.length === 0) {
|
||||
throw new CordovaError('No Java files found that extend CordovaActivity.');
|
||||
} else if(java_files.length > 1) {
|
||||
} else if (java_files.length > 1) {
|
||||
events.emit('log', 'Multiple candidate Java files that extend CordovaActivity found. Guessing at the first one, ' + java_files[0]);
|
||||
}
|
||||
|
||||
var destFile = path.join(locations.root, 'src', pkg.replace(/\./g, '/'), path.basename(java_files[0]));
|
||||
var destFile = path.join(locations.root, 'app', 'src', 'main', 'java', androidPkgName.replace(/\./g, '/'), path.basename(java_files[0]));
|
||||
shell.mkdir('-p', path.dirname(destFile));
|
||||
shell.sed(/package [\w\.]*;/, 'package ' + pkg + ';', java_files[0]).to(destFile);
|
||||
events.emit('verbose', 'Wrote out Android package name "' + pkg + '" to ' + destFile);
|
||||
shell.sed(/package [\w\.]*;/, 'package ' + androidPkgName + ';', java_files[0]).to(destFile);
|
||||
events.emit('verbose', 'Wrote out Android package name "' + androidPkgName + '" to ' + destFile);
|
||||
|
||||
if (orig_pkg !== pkg) {
|
||||
var removeOrigPkg = checkReqs.isWindows() || checkReqs.isDarwin() ?
|
||||
manifestId.toUpperCase() !== androidPkgName.toUpperCase() :
|
||||
manifestId !== androidPkgName;
|
||||
|
||||
if (removeOrigPkg) {
|
||||
// If package was name changed we need to remove old java with main activity
|
||||
shell.rm('-Rf',java_files[0]);
|
||||
shell.rm('-Rf', java_files[0]);
|
||||
// remove any empty directories
|
||||
var currentDir = path.dirname(java_files[0]);
|
||||
var sourcesRoot = path.resolve(locations.root, 'src');
|
||||
while(currentDir !== sourcesRoot) {
|
||||
if(fs.existsSync(currentDir) && fs.readdirSync(currentDir).length === 0) {
|
||||
while (currentDir !== sourcesRoot) {
|
||||
if (fs.existsSync(currentDir) && fs.readdirSync(currentDir).length === 0) {
|
||||
fs.rmdirSync(currentDir);
|
||||
currentDir = path.resolve(currentDir, '..');
|
||||
} else {
|
||||
@ -229,7 +240,7 @@ function updateProjectAccordingTo(platformConfig, locations) {
|
||||
// Consturct the default value for versionCode as
|
||||
// PATCH + MINOR * 100 + MAJOR * 10000
|
||||
// see http://developer.android.com/tools/publishing/versioning.html
|
||||
function default_versionCode(version) {
|
||||
function default_versionCode (version) {
|
||||
var nums = version.split('-')[0].split('.');
|
||||
var versionCode = 0;
|
||||
if (+nums[0]) {
|
||||
@ -246,7 +257,7 @@ function default_versionCode(version) {
|
||||
return versionCode;
|
||||
}
|
||||
|
||||
function getImageResourcePath(resourcesDir, type, density, name, sourceName) {
|
||||
function getImageResourcePath (resourcesDir, type, density, name, sourceName) {
|
||||
if (/\.9\.png$/.test(sourceName)) {
|
||||
name = name.replace(/\.png$/, '.9.png');
|
||||
}
|
||||
@ -254,7 +265,7 @@ function getImageResourcePath(resourcesDir, type, density, name, sourceName) {
|
||||
return resourcePath;
|
||||
}
|
||||
|
||||
function updateSplashes(cordovaProject, platformResourcesDir) {
|
||||
function updateSplashes (cordovaProject, platformResourcesDir) {
|
||||
var resources = cordovaProject.projectConfig.getSplashScreens('android');
|
||||
|
||||
// if there are "splash" elements in config.xml
|
||||
@ -270,7 +281,7 @@ function updateSplashes(cordovaProject, platformResourcesDir) {
|
||||
if (!resource.density) {
|
||||
return;
|
||||
}
|
||||
if (resource.density == 'mdpi') {
|
||||
if (resource.density === 'mdpi') {
|
||||
hadMdpi = true;
|
||||
}
|
||||
var targetPath = getImageResourcePath(
|
||||
@ -290,7 +301,7 @@ function updateSplashes(cordovaProject, platformResourcesDir) {
|
||||
resourceMap, { rootDir: cordovaProject.root }, logFileOp);
|
||||
}
|
||||
|
||||
function cleanSplashes(projectRoot, projectConfig, platformResourcesDir) {
|
||||
function cleanSplashes (projectRoot, projectConfig, platformResourcesDir) {
|
||||
var resources = projectConfig.getSplashScreens('android');
|
||||
if (resources.length > 0) {
|
||||
var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'drawable', 'screen.png');
|
||||
@ -302,7 +313,7 @@ function cleanSplashes(projectRoot, projectConfig, platformResourcesDir) {
|
||||
}
|
||||
}
|
||||
|
||||
function updateIcons(cordovaProject, platformResourcesDir) {
|
||||
function updateIcons (cordovaProject, platformResourcesDir) {
|
||||
var icons = cordovaProject.projectConfig.getIcons('android');
|
||||
|
||||
// if there are icon elements in config.xml
|
||||
@ -326,7 +337,7 @@ function updateIcons(cordovaProject, platformResourcesDir) {
|
||||
};
|
||||
// find the best matching icon for a given density or size
|
||||
// @output android_icons
|
||||
var parseIcon = function(icon, icon_size) {
|
||||
var parseIcon = function (icon, icon_size) {
|
||||
// do I have a platform icon for that density already
|
||||
var density = icon.density || sizeToDensityMap[icon_size];
|
||||
if (!density) {
|
||||
@ -341,7 +352,7 @@ function updateIcons(cordovaProject, platformResourcesDir) {
|
||||
};
|
||||
|
||||
// iterate over all icon elements to find the default icon and call parseIcon
|
||||
for (var i=0; i<icons.length; i++) {
|
||||
for (var i = 0; i < icons.length; i++) {
|
||||
var icon = icons[i];
|
||||
var size = icon.width;
|
||||
if (!size) {
|
||||
@ -378,7 +389,7 @@ function updateIcons(cordovaProject, platformResourcesDir) {
|
||||
resourceMap, { rootDir: cordovaProject.root }, logFileOp);
|
||||
}
|
||||
|
||||
function cleanIcons(projectRoot, projectConfig, platformResourcesDir) {
|
||||
function cleanIcons (projectRoot, projectConfig, platformResourcesDir) {
|
||||
var icons = projectConfig.getIcons('android');
|
||||
if (icons.length > 0) {
|
||||
var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'icon.png');
|
||||
@ -393,18 +404,16 @@ function cleanIcons(projectRoot, projectConfig, platformResourcesDir) {
|
||||
/**
|
||||
* Gets a map containing resources of a specified name from all drawable folders in a directory.
|
||||
*/
|
||||
function mapImageResources(rootDir, subDir, type, resourceName) {
|
||||
function mapImageResources (rootDir, subDir, type, resourceName) {
|
||||
var pathMap = {};
|
||||
shell.ls(path.join(rootDir, subDir, type + '-*'))
|
||||
.forEach(function (drawableFolder) {
|
||||
shell.ls(path.join(rootDir, subDir, type + '-*')).forEach(function (drawableFolder) {
|
||||
var imagePath = path.join(subDir, path.basename(drawableFolder), resourceName);
|
||||
pathMap[imagePath] = null;
|
||||
});
|
||||
return pathMap;
|
||||
}
|
||||
|
||||
|
||||
function updateFileResources(cordovaProject, platformDir) {
|
||||
function updateFileResources (cordovaProject, platformDir) {
|
||||
var files = cordovaProject.projectConfig.getFileResources('android');
|
||||
|
||||
// if there are resource-file elements in config.xml
|
||||
@ -414,7 +423,7 @@ function updateFileResources(cordovaProject, platformDir) {
|
||||
}
|
||||
|
||||
var resourceMap = {};
|
||||
files.forEach(function(res) {
|
||||
files.forEach(function (res) {
|
||||
var targetPath = path.join(platformDir, res.target);
|
||||
resourceMap[targetPath] = res.src;
|
||||
});
|
||||
@ -424,20 +433,20 @@ function updateFileResources(cordovaProject, platformDir) {
|
||||
resourceMap, { rootDir: cordovaProject.root }, logFileOp);
|
||||
}
|
||||
|
||||
|
||||
function cleanFileResources(projectRoot, projectConfig, platformDir) {
|
||||
var files = projectConfig.getFileResources('android');
|
||||
function cleanFileResources (projectRoot, projectConfig, platformDir) {
|
||||
var files = projectConfig.getFileResources('android', true);
|
||||
if (files.length > 0) {
|
||||
events.emit('verbose', 'Cleaning resource files at ' + platformDir);
|
||||
|
||||
var resourceMap = {};
|
||||
files.forEach(function(res) {
|
||||
files.forEach(function (res) {
|
||||
var filePath = path.join(platformDir, res.target);
|
||||
resourceMap[filePath] = null;
|
||||
});
|
||||
|
||||
FileUpdater.updatePaths(
|
||||
resourceMap, { rootDir: projectRoot, all: true}, logFileOp);
|
||||
resourceMap, {
|
||||
rootDir: projectRoot, all: true}, logFileOp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -452,7 +461,7 @@ function cleanFileResources(projectRoot, projectConfig, platformDir) {
|
||||
* default value, if there is no such preference. The default value is
|
||||
* 'singleTop'
|
||||
*/
|
||||
function findAndroidLaunchModePreference(platformConfig) {
|
||||
function findAndroidLaunchModePreference (platformConfig) {
|
||||
var launchMode = platformConfig.getPreference('AndroidLaunchMode');
|
||||
if (!launchMode) {
|
||||
// Return a default value
|
||||
|
@ -45,12 +45,12 @@ module.exports.retryPromise = function (attemts_left, promiseFunction) {
|
||||
return promiseFunction.apply(undefined, promiseFunctionArguments).then(
|
||||
|
||||
// on success pass results through
|
||||
function onFulfilled(value) {
|
||||
function onFulfilled (value) {
|
||||
return value;
|
||||
},
|
||||
|
||||
// on rejection either retry, or throw the error
|
||||
function onRejected(error) {
|
||||
function onRejected (error) {
|
||||
|
||||
attemts_left -= 1;
|
||||
|
||||
|
@ -21,14 +21,14 @@
|
||||
|
||||
/* jshint loopfunc:true */
|
||||
|
||||
var path = require('path'),
|
||||
build = require('./build'),
|
||||
emulator = require('./emulator'),
|
||||
device = require('./device'),
|
||||
Q = require('q'),
|
||||
events = require('cordova-common').events;
|
||||
var path = require('path');
|
||||
var build = require('./build');
|
||||
var emulator = require('./emulator');
|
||||
var device = require('./device');
|
||||
var Q = require('q');
|
||||
var events = require('cordova-common').events;
|
||||
|
||||
function getInstallTarget(runOptions) {
|
||||
function getInstallTarget (runOptions) {
|
||||
var install_target;
|
||||
if (runOptions.target) {
|
||||
install_target = runOptions.target;
|
||||
@ -51,17 +51,15 @@ function getInstallTarget(runOptions) {
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
module.exports.run = function(runOptions) {
|
||||
module.exports.run = function (runOptions) {
|
||||
|
||||
var self = this;
|
||||
var install_target = getInstallTarget(runOptions);
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
return Q().then(function () {
|
||||
if (!install_target) {
|
||||
// no target given, deploy to device if available, otherwise use the emulator.
|
||||
return device.list()
|
||||
.then(function(device_list) {
|
||||
return device.list().then(function (device_list) {
|
||||
if (device_list.length > 0) {
|
||||
events.emit('warn', 'No target specified, deploying to device \'' + device_list[0] + '\'.');
|
||||
install_target = device_list[0];
|
||||
@ -71,36 +69,31 @@ function getInstallTarget(runOptions) {
|
||||
}
|
||||
});
|
||||
}
|
||||
}).then(function() {
|
||||
if (install_target == '--device') {
|
||||
}).then(function () {
|
||||
if (install_target === '--device') {
|
||||
return device.resolveTarget(null);
|
||||
} else if (install_target == '--emulator') {
|
||||
} else if (install_target === '--emulator') {
|
||||
// Give preference to any already started emulators. Else, start one.
|
||||
return emulator.list_started()
|
||||
.then(function(started) {
|
||||
return emulator.list_started().then(function (started) {
|
||||
return started && started.length > 0 ? started[0] : emulator.start();
|
||||
}).then(function(emulatorId) {
|
||||
}).then(function (emulatorId) {
|
||||
return emulator.resolveTarget(emulatorId);
|
||||
});
|
||||
}
|
||||
// They specified a specific device/emulator ID.
|
||||
return device.list()
|
||||
.then(function(devices) {
|
||||
return device.list().then(function (devices) {
|
||||
if (devices.indexOf(install_target) > -1) {
|
||||
return device.resolveTarget(install_target);
|
||||
}
|
||||
return emulator.list_started()
|
||||
.then(function(started_emulators) {
|
||||
return emulator.list_started().then(function (started_emulators) {
|
||||
if (started_emulators.indexOf(install_target) > -1) {
|
||||
return emulator.resolveTarget(install_target);
|
||||
}
|
||||
return emulator.list_images()
|
||||
.then(function(avds) {
|
||||
return emulator.list_images().then(function (avds) {
|
||||
// if target emulator isn't started, then start it.
|
||||
for (var avd in avds) {
|
||||
if (avds[avd].name == install_target) {
|
||||
return emulator.start(install_target)
|
||||
.then(function(emulatorId) {
|
||||
if (avds[avd].name === install_target) {
|
||||
return emulator.start(install_target).then(function (emulatorId) {
|
||||
return emulator.resolveTarget(emulatorId);
|
||||
});
|
||||
}
|
||||
@ -109,16 +102,14 @@ function getInstallTarget(runOptions) {
|
||||
});
|
||||
});
|
||||
});
|
||||
}).then(function(resolvedTarget) {
|
||||
}).then(function (resolvedTarget) {
|
||||
// Better just call self.build, but we're doing some processing of
|
||||
// build results (according to platformApi spec) so they are in different
|
||||
// format than emulator.install expects.
|
||||
// TODO: Update emulator/device.install to handle this change
|
||||
return build.run.call(self, runOptions, resolvedTarget)
|
||||
.then(function(buildResults) {
|
||||
return build.run.call(self, runOptions, resolvedTarget).then(function (buildResults) {
|
||||
if (resolvedTarget.isEmulator) {
|
||||
return emulator.wait_for_boot(resolvedTarget.target)
|
||||
.then(function () {
|
||||
return emulator.wait_for_boot(resolvedTarget.target).then(function () {
|
||||
return emulator.install(resolvedTarget, buildResults);
|
||||
});
|
||||
}
|
||||
@ -127,7 +118,7 @@ function getInstallTarget(runOptions) {
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.help = function() {
|
||||
module.exports.help = function () {
|
||||
console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]) + ' [options]');
|
||||
console.log('Build options :');
|
||||
console.log(' --debug : Builds project in debug mode');
|
||||
|
@ -18,7 +18,7 @@
|
||||
@ECHO OFF
|
||||
SET script_path="%~dp0start-emulator"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
node %script_path% %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'start-emulator' script in 'cordova\lib' folder, aborting...>&2
|
||||
|
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
// Coho updates this line:
|
||||
var VERSION = "6.2.3";
|
||||
var VERSION = "7.0.0";
|
||||
|
||||
module.exports.version = VERSION;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#Tue Aug 22 17:22:32 EDT 2017
|
||||
#Fri Jan 05 03:48:26 EST 2018
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
||||
|
@ -1,49 +1,49 @@
|
||||
cordova.define('cordova/plugin_list', function(require, exports, module) {
|
||||
module.exports = [
|
||||
{
|
||||
"id": "cordova-plugin-device.device",
|
||||
"file": "plugins/cordova-plugin-device/www/device.js",
|
||||
"pluginId": "cordova-plugin-device",
|
||||
"clobbers": [
|
||||
"device"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cordova-plugin-splashscreen.SplashScreen",
|
||||
"file": "plugins/cordova-plugin-splashscreen/www/splashscreen.js",
|
||||
"pluginId": "cordova-plugin-splashscreen",
|
||||
"clobbers": [
|
||||
"navigator.splashscreen"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cordova-plugin-statusbar.statusbar",
|
||||
"file": "plugins/cordova-plugin-statusbar/www/statusbar.js",
|
||||
"pluginId": "cordova-plugin-statusbar",
|
||||
"clobbers": [
|
||||
"window.StatusBar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ionic-plugin-keyboard.keyboard",
|
||||
"file": "plugins/ionic-plugin-keyboard/www/android/keyboard.js",
|
||||
"pluginId": "ionic-plugin-keyboard",
|
||||
"clobbers": [
|
||||
"cordova.plugins.Keyboard"
|
||||
],
|
||||
"runs": true
|
||||
}
|
||||
{
|
||||
"id": "cordova-plugin-device.device",
|
||||
"file": "plugins/cordova-plugin-device/www/device.js",
|
||||
"pluginId": "cordova-plugin-device",
|
||||
"clobbers": [
|
||||
"device"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cordova-plugin-splashscreen.SplashScreen",
|
||||
"file": "plugins/cordova-plugin-splashscreen/www/splashscreen.js",
|
||||
"pluginId": "cordova-plugin-splashscreen",
|
||||
"clobbers": [
|
||||
"navigator.splashscreen"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cordova-plugin-statusbar.statusbar",
|
||||
"file": "plugins/cordova-plugin-statusbar/www/statusbar.js",
|
||||
"pluginId": "cordova-plugin-statusbar",
|
||||
"clobbers": [
|
||||
"window.StatusBar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ionic-plugin-keyboard.keyboard",
|
||||
"file": "plugins/ionic-plugin-keyboard/www/android/keyboard.js",
|
||||
"pluginId": "ionic-plugin-keyboard",
|
||||
"clobbers": [
|
||||
"cordova.plugins.Keyboard"
|
||||
],
|
||||
"runs": true
|
||||
}
|
||||
];
|
||||
module.exports.metadata =
|
||||
// TOP OF METADATA
|
||||
{
|
||||
"cordova-plugin-console": "1.0.5",
|
||||
"cordova-plugin-device": "1.1.4",
|
||||
"cordova-plugin-splashscreen": "4.0.3",
|
||||
"cordova-plugin-statusbar": "2.2.3",
|
||||
"cordova-plugin-whitelist": "1.3.1",
|
||||
"ionic-plugin-keyboard": "2.2.1",
|
||||
"cordova-plugin-crosswalk-webview": "2.3.0"
|
||||
"cordova-plugin-console": "1.1.0",
|
||||
"cordova-plugin-crosswalk-webview": "2.3.0",
|
||||
"cordova-plugin-device": "1.1.7",
|
||||
"cordova-plugin-splashscreen": "4.1.0",
|
||||
"cordova-plugin-statusbar": "2.4.1",
|
||||
"cordova-plugin-whitelist": "1.3.3",
|
||||
"ionic-plugin-keyboard": "2.2.1"
|
||||
};
|
||||
// BOTTOM OF METADATA
|
||||
});
|
@ -20,11 +20,11 @@ cordova.define("cordova-plugin-device.device", function(require, exports, module
|
||||
*
|
||||
*/
|
||||
|
||||
var argscheck = require('cordova/argscheck'),
|
||||
channel = require('cordova/channel'),
|
||||
utils = require('cordova/utils'),
|
||||
exec = require('cordova/exec'),
|
||||
cordova = require('cordova');
|
||||
var argscheck = require('cordova/argscheck');
|
||||
var channel = require('cordova/channel');
|
||||
var utils = require('cordova/utils');
|
||||
var exec = require('cordova/exec');
|
||||
var cordova = require('cordova');
|
||||
|
||||
channel.createSticky('onCordovaInfoReady');
|
||||
// Tell cordova channel to wait on the CordovaInfoReady event
|
||||
@ -35,7 +35,7 @@ channel.waitForInitialization('onCordovaInfoReady');
|
||||
* phone, etc.
|
||||
* @constructor
|
||||
*/
|
||||
function Device() {
|
||||
function Device () {
|
||||
this.available = false;
|
||||
this.platform = null;
|
||||
this.version = null;
|
||||
@ -48,10 +48,10 @@ function Device() {
|
||||
|
||||
var me = this;
|
||||
|
||||
channel.onCordovaReady.subscribe(function() {
|
||||
me.getInfo(function(info) {
|
||||
//ignoring info.cordova returning from native, we should use value from cordova.version defined in cordova.js
|
||||
//TODO: CB-5105 native implementations should not return info.cordova
|
||||
channel.onCordovaReady.subscribe(function () {
|
||||
me.getInfo(function (info) {
|
||||
// ignoring info.cordova returning from native, we should use value from cordova.version defined in cordova.js
|
||||
// TODO: CB-5105 native implementations should not return info.cordova
|
||||
var buildLabel = cordova.version;
|
||||
me.available = true;
|
||||
me.platform = info.platform;
|
||||
@ -63,9 +63,9 @@ function Device() {
|
||||
me.manufacturer = info.manufacturer || 'unknown';
|
||||
me.serial = info.serial || 'unknown';
|
||||
channel.onCordovaInfoReady.fire();
|
||||
},function(e) {
|
||||
}, function (e) {
|
||||
me.available = false;
|
||||
utils.alert("[ERROR] Error initializing Cordova: " + e);
|
||||
utils.alert('[ERROR] Error initializing Cordova: ' + e);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -76,9 +76,9 @@ function Device() {
|
||||
* @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) {
|
||||
Device.prototype.getInfo = function (successCallback, errorCallback) {
|
||||
argscheck.checkArgs('fF', 'Device.getInfo', arguments);
|
||||
exec(successCallback, errorCallback, "Device", "getDeviceInfo", []);
|
||||
exec(successCallback, errorCallback, 'Device', 'getDeviceInfo', []);
|
||||
};
|
||||
|
||||
module.exports = new Device();
|
||||
|
@ -1,15 +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
|
||||
# This file was originally created by the Android Tools, but is now
|
||||
# used by cordova-android to manage the state of the various third party
|
||||
# libraries used in your application
|
||||
|
||||
# This is the Library Module that contains the Cordova Library, this is not
|
||||
# required when using an AAR
|
||||
|
||||
# This is the application project. This is only required for Android Studio Gradle projects
|
||||
|
||||
# Project target.
|
||||
target=android-25
|
||||
target=android-26
|
||||
android.library.reference.1=CordovaLib
|
||||
android.library.reference.2=app
|
||||
cordova.gradle.include.1=cordova-plugin-crosswalk-webview/dynamicbible-xwalk.gradle
|
Before Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 99 KiB |
Before Width: | Height: | Size: 132 KiB |
Before Width: | Height: | Size: 172 KiB |
Before Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 131 KiB |
Before Width: | Height: | Size: 171 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 14 KiB |
@ -1,6 +0,0 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<resources>
|
||||
<string name="app_name">Dynamic Bible</string>
|
||||
<string name="launcher_name">@string/app_name</string>
|
||||
<string name="activity_name">@string/launcher_name</string>
|
||||
</resources>
|
@ -1,90 +0,0 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<widget id="walljm.dynamicbible" version="3.0.3" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
|
||||
<feature name="Device">
|
||||
<param name="android-package" value="org.apache.cordova.device.Device" />
|
||||
</feature>
|
||||
<feature name="SplashScreen">
|
||||
<param name="android-package" value="org.apache.cordova.splashscreen.SplashScreen" />
|
||||
<param name="onload" value="true" />
|
||||
</feature>
|
||||
<feature name="StatusBar">
|
||||
<param name="android-package" value="org.apache.cordova.statusbar.StatusBar" />
|
||||
<param name="onload" value="true" />
|
||||
</feature>
|
||||
<feature name="Whitelist">
|
||||
<param name="android-package" value="org.apache.cordova.whitelist.WhitelistPlugin" />
|
||||
<param name="onload" value="true" />
|
||||
</feature>
|
||||
<feature name="Keyboard">
|
||||
<param name="android-package" value="io.ionic.keyboard.IonicKeyboard" />
|
||||
<param name="onload" value="true" />
|
||||
</feature>
|
||||
<name>Dynamic Bible</name>
|
||||
<description>A bible app designed for bible study</description>
|
||||
<author email="jason@walljm.com" href="http://dynamicbible.com/">Jason Wall</author>
|
||||
<content src="index.html" />
|
||||
<access origin="*" />
|
||||
<allow-intent href="http://*/*" />
|
||||
<allow-intent href="https://*/*" />
|
||||
<allow-intent href="tel:*" />
|
||||
<allow-intent href="sms:*" />
|
||||
<allow-intent href="mailto:*" />
|
||||
<allow-intent href="geo:*" />
|
||||
<allow-intent href="market:*" />
|
||||
<icon density="ldpi" src="resources\android\icon\drawable-ldpi-icon.png" />
|
||||
<icon density="mdpi" src="resources\android\icon\drawable-mdpi-icon.png" />
|
||||
<icon density="hdpi" src="resources\android\icon\drawable-hdpi-icon.png" />
|
||||
<icon density="xhdpi" src="resources\android\icon\drawable-xhdpi-icon.png" />
|
||||
<icon density="xxhdpi" src="resources\android\icon\drawable-xxhdpi-icon.png" />
|
||||
<icon density="xxxhdpi" src="resources\android\icon\drawable-xxxhdpi-icon.png" />
|
||||
<splash density="land-ldpi" src="resources\android\splash\drawable-land-ldpi-screen.png" />
|
||||
<splash density="land-mdpi" src="resources\android\splash\drawable-land-mdpi-screen.png" />
|
||||
<splash density="land-hdpi" src="resources\android\splash\drawable-land-hdpi-screen.png" />
|
||||
<splash density="land-xhdpi" src="resources\android\splash\drawable-land-xhdpi-screen.png" />
|
||||
<splash density="land-xxhdpi" src="resources\android\splash\drawable-land-xxhdpi-screen.png" />
|
||||
<splash density="land-xxxhdpi" src="resources\android\splash\drawable-land-xxxhdpi-screen.png" />
|
||||
<splash density="port-ldpi" src="resources\android\splash\drawable-port-ldpi-screen.png" />
|
||||
<splash density="port-mdpi" src="resources\android\splash\drawable-port-mdpi-screen.png" />
|
||||
<splash density="port-hdpi" src="resources\android\splash\drawable-port-hdpi-screen.png" />
|
||||
<splash density="port-xhdpi" src="resources\android\splash\drawable-port-xhdpi-screen.png" />
|
||||
<splash density="port-xxhdpi" src="resources\android\splash\drawable-port-xxhdpi-screen.png" />
|
||||
<splash density="port-xxxhdpi" src="resources\android\splash\drawable-port-xxxhdpi-screen.png" />
|
||||
<icon density="ldpi" src="resources/android/icon/drawable-ldpi-icon.png" />
|
||||
<icon density="mdpi" src="resources/android/icon/drawable-mdpi-icon.png" />
|
||||
<icon density="hdpi" src="resources/android/icon/drawable-hdpi-icon.png" />
|
||||
<icon density="xhdpi" src="resources/android/icon/drawable-xhdpi-icon.png" />
|
||||
<icon density="xxhdpi" src="resources/android/icon/drawable-xxhdpi-icon.png" />
|
||||
<icon density="xxxhdpi" src="resources/android/icon/drawable-xxxhdpi-icon.png" />
|
||||
<splash density="land-ldpi" src="resources/android/splash/drawable-land-ldpi-screen.png" />
|
||||
<splash density="land-mdpi" src="resources/android/splash/drawable-land-mdpi-screen.png" />
|
||||
<splash density="land-hdpi" src="resources/android/splash/drawable-land-hdpi-screen.png" />
|
||||
<splash density="land-xhdpi" src="resources/android/splash/drawable-land-xhdpi-screen.png" />
|
||||
<splash density="land-xxhdpi" src="resources/android/splash/drawable-land-xxhdpi-screen.png" />
|
||||
<splash density="land-xxxhdpi" src="resources/android/splash/drawable-land-xxxhdpi-screen.png" />
|
||||
<splash density="port-ldpi" src="resources/android/splash/drawable-port-ldpi-screen.png" />
|
||||
<splash density="port-mdpi" src="resources/android/splash/drawable-port-mdpi-screen.png" />
|
||||
<splash density="port-hdpi" src="resources/android/splash/drawable-port-hdpi-screen.png" />
|
||||
<splash density="port-xhdpi" src="resources/android/splash/drawable-port-xhdpi-screen.png" />
|
||||
<splash density="port-xxhdpi" src="resources/android/splash/drawable-port-xxhdpi-screen.png" />
|
||||
<splash density="port-xxxhdpi" src="resources/android/splash/drawable-port-xxxhdpi-screen.png" />
|
||||
<preference name="loglevel" value="DEBUG" />
|
||||
<preference name="webView" value="org.crosswalk.engine.XWalkWebViewEngine" />
|
||||
<preference name="xwalkVersion" value="23+" />
|
||||
<preference name="xwalkLiteVersion" value="xwalk_core_library_canary:17+" />
|
||||
<preference name="xwalkCommandLine" value="--disable-pull-to-refresh-effect" />
|
||||
<preference name="xwalkMode" value="embedded" />
|
||||
<preference name="xwalkMultipleApk" value="true" />
|
||||
<preference name="webviewbounce" value="false" />
|
||||
<preference name="UIWebViewBounce" value="false" />
|
||||
<preference name="DisallowOverscroll" value="true" />
|
||||
<preference name="android-minSdkVersion" value="16" />
|
||||
<preference name="BackupWebStorage" value="none" />
|
||||
<preference name="SplashMaintainAspectRatio" value="true" />
|
||||
<preference name="FadeSplashScreenDuration" value="300" />
|
||||
<preference name="SplashScreen" value="screen" />
|
||||
<preference name="SplashScreenDelay" value="12000" />
|
||||
<preference name="ShowSplashScreen" value="true" />
|
||||
<preference name="AutoHideSplashScreen" value="false" />
|
||||
<preference name="SplashShowOnlyFirstTime" value="false" />
|
||||
<preference name="FadeSplashScreen" value="false" />
|
||||
</widget>
|
@ -1,3 +1,4 @@
|
||||
// GENERATED FILE - DO NOT EDIT
|
||||
include ":"
|
||||
include ":CordovaLib"
|
||||
include ":app"
|
||||
|
@ -1,130 +0,0 @@
|
||||
package io.ionic.keyboard;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.PluginResult;
|
||||
import org.apache.cordova.PluginResult.Status;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
// import additionally required classes for calculating screen height
|
||||
import android.view.Display;
|
||||
import android.graphics.Point;
|
||||
import android.os.Build;
|
||||
|
||||
public class IonicKeyboard extends CordovaPlugin {
|
||||
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
}
|
||||
|
||||
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
if ("close".equals(action)) {
|
||||
cordova.getThreadPool().execute(new Runnable() {
|
||||
public void run() {
|
||||
//http://stackoverflow.com/a/7696791/1091751
|
||||
InputMethodManager inputManager = (InputMethodManager) cordova.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
View v = cordova.getActivity().getCurrentFocus();
|
||||
|
||||
if (v == null) {
|
||||
callbackContext.error("No current focus");
|
||||
} else {
|
||||
inputManager.hideSoftInputFromWindow(v.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
|
||||
callbackContext.success(); // Thread-safe.
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
if ("show".equals(action)) {
|
||||
cordova.getThreadPool().execute(new Runnable() {
|
||||
public void run() {
|
||||
((InputMethodManager) cordova.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE)).toggleSoftInput(0, InputMethodManager.HIDE_IMPLICIT_ONLY);
|
||||
callbackContext.success(); // Thread-safe.
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
if ("init".equals(action)) {
|
||||
cordova.getThreadPool().execute(new Runnable() {
|
||||
public void run() {
|
||||
//calculate density-independent pixels (dp)
|
||||
//http://developer.android.com/guide/practices/screens_support.html
|
||||
DisplayMetrics dm = new DisplayMetrics();
|
||||
cordova.getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm);
|
||||
final float density = dm.density;
|
||||
|
||||
//http://stackoverflow.com/a/4737265/1091751 detect if keyboard is showing
|
||||
final View rootView = cordova.getActivity().getWindow().getDecorView().findViewById(android.R.id.content).getRootView();
|
||||
OnGlobalLayoutListener list = new OnGlobalLayoutListener() {
|
||||
int previousHeightDiff = 0;
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
Rect r = new Rect();
|
||||
//r will be populated with the coordinates of your view that area still visible.
|
||||
rootView.getWindowVisibleDisplayFrame(r);
|
||||
|
||||
PluginResult result;
|
||||
|
||||
// cache properties for later use
|
||||
int rootViewHeight = rootView.getRootView().getHeight();
|
||||
int resultBottom = r.bottom;
|
||||
|
||||
// calculate screen height differently for android versions >= 21: Lollipop 5.x, Marshmallow 6.x
|
||||
//http://stackoverflow.com/a/29257533/3642890 beware of nexus 5
|
||||
int screenHeight;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
Display display = cordova.getActivity().getWindowManager().getDefaultDisplay();
|
||||
Point size = new Point();
|
||||
display.getSize(size);
|
||||
screenHeight = size.y;
|
||||
} else {
|
||||
screenHeight = rootViewHeight;
|
||||
}
|
||||
|
||||
int heightDiff = screenHeight - resultBottom;
|
||||
|
||||
int pixelHeightDiff = (int)(heightDiff / density);
|
||||
if (pixelHeightDiff > 100 && pixelHeightDiff != previousHeightDiff) { // if more than 100 pixels, its probably a keyboard...
|
||||
String msg = "S" + Integer.toString(pixelHeightDiff);
|
||||
result = new PluginResult(PluginResult.Status.OK, msg);
|
||||
result.setKeepCallback(true);
|
||||
callbackContext.sendPluginResult(result);
|
||||
}
|
||||
else if ( pixelHeightDiff != previousHeightDiff && ( previousHeightDiff - pixelHeightDiff ) > 100 ){
|
||||
String msg = "H";
|
||||
result = new PluginResult(PluginResult.Status.OK, msg);
|
||||
result.setKeepCallback(true);
|
||||
callbackContext.sendPluginResult(result);
|
||||
}
|
||||
previousHeightDiff = pixelHeightDiff;
|
||||
}
|
||||
};
|
||||
|
||||
rootView.getViewTreeObserver().addOnGlobalLayoutListener(list);
|
||||
|
||||
|
||||
PluginResult dataResult = new PluginResult(PluginResult.Status.OK);
|
||||
dataResult.setKeepCallback(true);
|
||||
callbackContext.sendPluginResult(dataResult);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false; // Returning false results in a "MethodNotFound" error.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,174 +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.
|
||||
*/
|
||||
package org.apache.cordova.device;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.provider.Settings;
|
||||
|
||||
public class Device extends CordovaPlugin {
|
||||
public static final String TAG = "Device";
|
||||
|
||||
public static String platform; // Device OS
|
||||
public static String uuid; // Device UUID
|
||||
|
||||
private static final String ANDROID_PLATFORM = "Android";
|
||||
private static final String AMAZON_PLATFORM = "amazon-fireos";
|
||||
private static final String AMAZON_DEVICE = "Amazon";
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public Device() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context of the Command. This can then be used to do things like
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param cordova The context of the main Activity.
|
||||
* @param webView The CordovaWebView Cordova is running in.
|
||||
*/
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
Device.uuid = getUuid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackContext The callback id used when calling back into JavaScript.
|
||||
* @return True if the action was valid, false if not.
|
||||
*/
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
if ("getDeviceInfo".equals(action)) {
|
||||
JSONObject r = new JSONObject();
|
||||
r.put("uuid", Device.uuid);
|
||||
r.put("version", this.getOSVersion());
|
||||
r.put("platform", this.getPlatform());
|
||||
r.put("model", this.getModel());
|
||||
r.put("manufacturer", this.getManufacturer());
|
||||
r.put("isVirtual", this.isVirtual());
|
||||
r.put("serial", this.getSerialNumber());
|
||||
callbackContext.success(r);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get the OS name.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getPlatform() {
|
||||
String platform;
|
||||
if (isAmazonDevice()) {
|
||||
platform = AMAZON_PLATFORM;
|
||||
} else {
|
||||
platform = ANDROID_PLATFORM;
|
||||
}
|
||||
return platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the device's Universally Unique Identifier (UUID).
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getUuid() {
|
||||
String uuid = Settings.Secure.getString(this.cordova.getActivity().getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
String model = android.os.Build.MODEL;
|
||||
return model;
|
||||
}
|
||||
|
||||
public String getProductName() {
|
||||
String productname = android.os.Build.PRODUCT;
|
||||
return productname;
|
||||
}
|
||||
|
||||
public String getManufacturer() {
|
||||
String manufacturer = android.os.Build.MANUFACTURER;
|
||||
return manufacturer;
|
||||
}
|
||||
|
||||
public String getSerialNumber() {
|
||||
String serial = android.os.Build.SERIAL;
|
||||
return serial;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the OS version.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getOSVersion() {
|
||||
String osversion = android.os.Build.VERSION.RELEASE;
|
||||
return osversion;
|
||||
}
|
||||
|
||||
public String getSDKVersion() {
|
||||
@SuppressWarnings("deprecation")
|
||||
String sdkversion = android.os.Build.VERSION.SDK;
|
||||
return sdkversion;
|
||||
}
|
||||
|
||||
public String getTimeZoneID() {
|
||||
TimeZone tz = TimeZone.getDefault();
|
||||
return (tz.getID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to check if the device is manufactured by Amazon
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isAmazonDevice() {
|
||||
if (android.os.Build.MANUFACTURER.equals(AMAZON_DEVICE)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isVirtual() {
|
||||
return android.os.Build.FINGERPRINT.contains("generic") ||
|
||||
android.os.Build.PRODUCT.contains("sdk");
|
||||
}
|
||||
|
||||
}
|
@ -1,385 +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.
|
||||
*/
|
||||
|
||||
package org.apache.cordova.splashscreen;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Handler;
|
||||
import android.view.Display;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class SplashScreen extends CordovaPlugin {
|
||||
private static final String LOG_TAG = "SplashScreen";
|
||||
// Cordova 3.x.x has a copy of this plugin bundled with it (SplashScreenInternal.java).
|
||||
// Enable functionality only if running on 4.x.x.
|
||||
private static final boolean HAS_BUILT_IN_SPLASH_SCREEN = Integer.valueOf(CordovaWebView.CORDOVA_VERSION.split("\\.")[0]) < 4;
|
||||
private static final int DEFAULT_SPLASHSCREEN_DURATION = 3000;
|
||||
private static final int DEFAULT_FADE_DURATION = 500;
|
||||
private static Dialog splashDialog;
|
||||
private static ProgressDialog spinnerDialog;
|
||||
private static boolean firstShow = true;
|
||||
private static boolean lastHideAfterDelay; // https://issues.apache.org/jira/browse/CB-9094
|
||||
|
||||
/**
|
||||
* Displays the splash drawable.
|
||||
*/
|
||||
private ImageView splashImageView;
|
||||
|
||||
/**
|
||||
* Remember last device orientation to detect orientation changes.
|
||||
*/
|
||||
private int orientation;
|
||||
|
||||
// Helper to be compile-time compatible with both Cordova 3.x and 4.x.
|
||||
private View getView() {
|
||||
try {
|
||||
return (View)webView.getClass().getMethod("getView").invoke(webView);
|
||||
} catch (Exception e) {
|
||||
return (View)webView;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void pluginInitialize() {
|
||||
if (HAS_BUILT_IN_SPLASH_SCREEN) {
|
||||
return;
|
||||
}
|
||||
// Make WebView invisible while loading URL
|
||||
// CB-11326 Ensure we're calling this on UI thread
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
getView().setVisibility(View.INVISIBLE);
|
||||
}
|
||||
});
|
||||
int drawableId = preferences.getInteger("SplashDrawableId", 0);
|
||||
if (drawableId == 0) {
|
||||
String splashResource = preferences.getString("SplashScreen", "screen");
|
||||
if (splashResource != null) {
|
||||
drawableId = cordova.getActivity().getResources().getIdentifier(splashResource, "drawable", cordova.getActivity().getClass().getPackage().getName());
|
||||
if (drawableId == 0) {
|
||||
drawableId = cordova.getActivity().getResources().getIdentifier(splashResource, "drawable", cordova.getActivity().getPackageName());
|
||||
}
|
||||
preferences.set("SplashDrawableId", drawableId);
|
||||
}
|
||||
}
|
||||
|
||||
// Save initial orientation.
|
||||
orientation = cordova.getActivity().getResources().getConfiguration().orientation;
|
||||
|
||||
if (firstShow) {
|
||||
boolean autoHide = preferences.getBoolean("AutoHideSplashScreen", true);
|
||||
showSplashScreen(autoHide);
|
||||
}
|
||||
|
||||
if (preferences.getBoolean("SplashShowOnlyFirstTime", true)) {
|
||||
firstShow = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorter way to check value of "SplashMaintainAspectRatio" preference.
|
||||
*/
|
||||
private boolean isMaintainAspectRatio () {
|
||||
return preferences.getBoolean("SplashMaintainAspectRatio", false);
|
||||
}
|
||||
|
||||
private int getFadeDuration () {
|
||||
int fadeSplashScreenDuration = preferences.getBoolean("FadeSplashScreen", true) ?
|
||||
preferences.getInteger("FadeSplashScreenDuration", DEFAULT_FADE_DURATION) : 0;
|
||||
|
||||
if (fadeSplashScreenDuration < 30) {
|
||||
// [CB-9750] This value used to be in decimal seconds, so we will assume that if someone specifies 10
|
||||
// they mean 10 seconds, and not the meaningless 10ms
|
||||
fadeSplashScreenDuration *= 1000;
|
||||
}
|
||||
|
||||
return fadeSplashScreenDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause(boolean multitasking) {
|
||||
if (HAS_BUILT_IN_SPLASH_SCREEN) {
|
||||
return;
|
||||
}
|
||||
// hide the splash screen to avoid leaking a window
|
||||
this.removeSplashScreen(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (HAS_BUILT_IN_SPLASH_SCREEN) {
|
||||
return;
|
||||
}
|
||||
// hide the splash screen to avoid leaking a window
|
||||
this.removeSplashScreen(true);
|
||||
// If we set this to true onDestroy, we lose track when we go from page to page!
|
||||
//firstShow = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
if (action.equals("hide")) {
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
webView.postMessage("splashscreen", "hide");
|
||||
}
|
||||
});
|
||||
} else if (action.equals("show")) {
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
webView.postMessage("splashscreen", "show");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
callbackContext.success();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onMessage(String id, Object data) {
|
||||
if (HAS_BUILT_IN_SPLASH_SCREEN) {
|
||||
return null;
|
||||
}
|
||||
if ("splashscreen".equals(id)) {
|
||||
if ("hide".equals(data.toString())) {
|
||||
this.removeSplashScreen(false);
|
||||
} else {
|
||||
this.showSplashScreen(false);
|
||||
}
|
||||
} else if ("spinner".equals(id)) {
|
||||
if ("stop".equals(data.toString())) {
|
||||
getView().setVisibility(View.VISIBLE);
|
||||
}
|
||||
} else if ("onReceivedError".equals(id)) {
|
||||
this.spinnerStop();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Don't add @Override so that plugin still compiles on 3.x.x for a while
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
if (newConfig.orientation != orientation) {
|
||||
orientation = newConfig.orientation;
|
||||
|
||||
// Splash drawable may change with orientation, so reload it.
|
||||
if (splashImageView != null) {
|
||||
int drawableId = preferences.getInteger("SplashDrawableId", 0);
|
||||
if (drawableId != 0) {
|
||||
splashImageView.setImageDrawable(cordova.getActivity().getResources().getDrawable(drawableId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeSplashScreen(final boolean forceHideImmediately) {
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
if (splashDialog != null && splashDialog.isShowing()) {
|
||||
final int fadeSplashScreenDuration = getFadeDuration();
|
||||
// CB-10692 If the plugin is being paused/destroyed, skip the fading and hide it immediately
|
||||
if (fadeSplashScreenDuration > 0 && forceHideImmediately == false) {
|
||||
AlphaAnimation fadeOut = new AlphaAnimation(1, 0);
|
||||
fadeOut.setInterpolator(new DecelerateInterpolator());
|
||||
fadeOut.setDuration(fadeSplashScreenDuration);
|
||||
|
||||
splashImageView.setAnimation(fadeOut);
|
||||
splashImageView.startAnimation(fadeOut);
|
||||
|
||||
fadeOut.setAnimationListener(new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
spinnerStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
if (splashDialog != null && splashDialog.isShowing()) {
|
||||
splashDialog.dismiss();
|
||||
splashDialog = null;
|
||||
splashImageView = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
spinnerStop();
|
||||
splashDialog.dismiss();
|
||||
splashDialog = null;
|
||||
splashImageView = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the splash screen over the full Activity
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
private void showSplashScreen(final boolean hideAfterDelay) {
|
||||
final int splashscreenTime = preferences.getInteger("SplashScreenDelay", DEFAULT_SPLASHSCREEN_DURATION);
|
||||
final int drawableId = preferences.getInteger("SplashDrawableId", 0);
|
||||
|
||||
final int fadeSplashScreenDuration = getFadeDuration();
|
||||
final int effectiveSplashDuration = Math.max(0, splashscreenTime - fadeSplashScreenDuration);
|
||||
|
||||
lastHideAfterDelay = hideAfterDelay;
|
||||
|
||||
// If the splash dialog is showing don't try to show it again
|
||||
if (splashDialog != null && splashDialog.isShowing()) {
|
||||
return;
|
||||
}
|
||||
if (drawableId == 0 || (splashscreenTime <= 0 && hideAfterDelay)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
// Get reference to display
|
||||
Display display = cordova.getActivity().getWindowManager().getDefaultDisplay();
|
||||
Context context = webView.getContext();
|
||||
|
||||
// Use an ImageView to render the image because of its flexible scaling options.
|
||||
splashImageView = new ImageView(context);
|
||||
splashImageView.setImageResource(drawableId);
|
||||
LayoutParams layoutParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
splashImageView.setLayoutParams(layoutParams);
|
||||
|
||||
splashImageView.setMinimumHeight(display.getHeight());
|
||||
splashImageView.setMinimumWidth(display.getWidth());
|
||||
|
||||
// TODO: Use the background color of the webView's parent instead of using the preference.
|
||||
splashImageView.setBackgroundColor(preferences.getInteger("backgroundColor", Color.BLACK));
|
||||
|
||||
if (isMaintainAspectRatio()) {
|
||||
// CENTER_CROP scale mode is equivalent to CSS "background-size:cover"
|
||||
splashImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
}
|
||||
else {
|
||||
// FIT_XY scales image non-uniformly to fit into image view.
|
||||
splashImageView.setScaleType(ImageView.ScaleType.FIT_XY);
|
||||
}
|
||||
|
||||
// Create and show the dialog
|
||||
splashDialog = new Dialog(context, android.R.style.Theme_Translucent_NoTitleBar);
|
||||
// check to see if the splash screen should be full screen
|
||||
if ((cordova.getActivity().getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
== WindowManager.LayoutParams.FLAG_FULLSCREEN) {
|
||||
splashDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
}
|
||||
splashDialog.setContentView(splashImageView);
|
||||
splashDialog.setCancelable(false);
|
||||
splashDialog.show();
|
||||
|
||||
if (preferences.getBoolean("ShowSplashScreenSpinner", true)) {
|
||||
spinnerStart();
|
||||
}
|
||||
|
||||
// Set Runnable to remove splash screen just in case
|
||||
if (hideAfterDelay) {
|
||||
final Handler handler = new Handler();
|
||||
handler.postDelayed(new Runnable() {
|
||||
public void run() {
|
||||
if (lastHideAfterDelay) {
|
||||
removeSplashScreen(false);
|
||||
}
|
||||
}
|
||||
}, effectiveSplashDuration);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Show only spinner in the center of the screen
|
||||
private void spinnerStart() {
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
spinnerStop();
|
||||
|
||||
spinnerDialog = new ProgressDialog(webView.getContext());
|
||||
spinnerDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
spinnerDialog = null;
|
||||
}
|
||||
});
|
||||
|
||||
spinnerDialog.setCancelable(false);
|
||||
spinnerDialog.setIndeterminate(true);
|
||||
|
||||
RelativeLayout centeredLayout = new RelativeLayout(cordova.getActivity());
|
||||
centeredLayout.setGravity(Gravity.CENTER);
|
||||
centeredLayout.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
|
||||
|
||||
ProgressBar progressBar = new ProgressBar(webView.getContext());
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
|
||||
progressBar.setLayoutParams(layoutParams);
|
||||
|
||||
centeredLayout.addView(progressBar);
|
||||
|
||||
spinnerDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
|
||||
spinnerDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
|
||||
spinnerDialog.show();
|
||||
spinnerDialog.setContentView(centeredLayout);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void spinnerStop() {
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
if (spinnerDialog != null && spinnerDialog.isShowing()) {
|
||||
spinnerDialog.dismiss();
|
||||
spinnerDialog = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,201 +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.
|
||||
*
|
||||
*/
|
||||
package org.apache.cordova.statusbar;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaArgs;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.LOG;
|
||||
import org.apache.cordova.PluginResult;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class StatusBar extends CordovaPlugin {
|
||||
private static final String TAG = "StatusBar";
|
||||
|
||||
/**
|
||||
* Sets the context of the Command. This can then be used to do things like
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param cordova The context of the main Activity.
|
||||
* @param webView The CordovaWebView Cordova is running in.
|
||||
*/
|
||||
@Override
|
||||
public void initialize(final CordovaInterface cordova, CordovaWebView webView) {
|
||||
LOG.v(TAG, "StatusBar: initialization");
|
||||
super.initialize(cordova, webView);
|
||||
|
||||
this.cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Clear flag FLAG_FORCE_NOT_FULLSCREEN which is set initially
|
||||
// by the Cordova.
|
||||
Window window = cordova.getActivity().getWindow();
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
|
||||
// Read 'StatusBarBackgroundColor' from config.xml, default is #000000.
|
||||
setStatusBarBackgroundColor(preferences.getString("StatusBarBackgroundColor", "#000000"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackContext The callback id used when calling back into JavaScript.
|
||||
* @return True if the action was valid, false otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean execute(final String action, final CordovaArgs args, final CallbackContext callbackContext) throws JSONException {
|
||||
LOG.v(TAG, "Executing action: " + action);
|
||||
final Activity activity = this.cordova.getActivity();
|
||||
final Window window = activity.getWindow();
|
||||
|
||||
if ("_ready".equals(action)) {
|
||||
boolean statusBarVisible = (window.getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) == 0;
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, statusBarVisible));
|
||||
return true;
|
||||
}
|
||||
|
||||
if ("show".equals(action)) {
|
||||
this.cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// SYSTEM_UI_FLAG_FULLSCREEN is available since JellyBean, but we
|
||||
// use KitKat here to be aligned with "Fullscreen" preference
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
int uiOptions = window.getDecorView().getSystemUiVisibility();
|
||||
uiOptions &= ~View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
|
||||
uiOptions &= ~View.SYSTEM_UI_FLAG_FULLSCREEN;
|
||||
|
||||
window.getDecorView().setSystemUiVisibility(uiOptions);
|
||||
}
|
||||
|
||||
// CB-11197 We still need to update LayoutParams to force status bar
|
||||
// to be hidden when entering e.g. text fields
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
if ("hide".equals(action)) {
|
||||
this.cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// SYSTEM_UI_FLAG_FULLSCREEN is available since JellyBean, but we
|
||||
// use KitKat here to be aligned with "Fullscreen" preference
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
int uiOptions = window.getDecorView().getSystemUiVisibility()
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN;
|
||||
|
||||
window.getDecorView().setSystemUiVisibility(uiOptions);
|
||||
}
|
||||
|
||||
// CB-11197 We still need to update LayoutParams to force status bar
|
||||
// to be hidden when entering e.g. text fields
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
if ("backgroundColorByHexString".equals(action)) {
|
||||
this.cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
setStatusBarBackgroundColor(args.getString(0));
|
||||
} catch (JSONException ignore) {
|
||||
LOG.e(TAG, "Invalid hexString argument, use f.i. '#777777'");
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
if ("overlaysWebView".equals(action)) {
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
this.cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
setStatusBarTransparent(args.getBoolean(0));
|
||||
} catch (JSONException ignore) {
|
||||
LOG.e(TAG, "Invalid boolean argument");
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
else return args.getBoolean(0) == false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setStatusBarBackgroundColor(final String colorPref) {
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
if (colorPref != null && !colorPref.isEmpty()) {
|
||||
final Window window = cordova.getActivity().getWindow();
|
||||
// Method and constants not available on all SDKs but we want to be able to compile this code with any SDK
|
||||
window.clearFlags(0x04000000); // SDK 19: WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
window.addFlags(0x80000000); // SDK 21: WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
||||
try {
|
||||
// Using reflection makes sure any 5.0+ device will work without having to compile with SDK level 21
|
||||
window.getClass().getMethod("setStatusBarColor", int.class).invoke(window, Color.parseColor(colorPref));
|
||||
} catch (IllegalArgumentException ignore) {
|
||||
LOG.e(TAG, "Invalid hexString argument, use f.i. '#999999'");
|
||||
} catch (Exception ignore) {
|
||||
// this should not happen, only in case Android removes this method in a version > 21
|
||||
LOG.w(TAG, "Method window.setStatusBarColor not found for SDK level " + Build.VERSION.SDK_INT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setStatusBarTransparent(final boolean transparent) {
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
final Window window = cordova.getActivity().getWindow();
|
||||
if (transparent) {
|
||||
window.getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
window.setStatusBarColor(Color.TRANSPARENT);
|
||||
}
|
||||
else {
|
||||
window.getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,161 +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.
|
||||
*/
|
||||
|
||||
package org.apache.cordova.whitelist;
|
||||
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.ConfigXmlParser;
|
||||
import org.apache.cordova.LOG;
|
||||
import org.apache.cordova.Whitelist;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class WhitelistPlugin extends CordovaPlugin {
|
||||
private static final String LOG_TAG = "WhitelistPlugin";
|
||||
private Whitelist allowedNavigations;
|
||||
private Whitelist allowedIntents;
|
||||
private Whitelist allowedRequests;
|
||||
|
||||
// Used when instantiated via reflection by PluginManager
|
||||
public WhitelistPlugin() {
|
||||
}
|
||||
// These can be used by embedders to allow Java-configuration of whitelists.
|
||||
public WhitelistPlugin(Context context) {
|
||||
this(new Whitelist(), new Whitelist(), null);
|
||||
new CustomConfigXmlParser().parse(context);
|
||||
}
|
||||
public WhitelistPlugin(XmlPullParser xmlParser) {
|
||||
this(new Whitelist(), new Whitelist(), null);
|
||||
new CustomConfigXmlParser().parse(xmlParser);
|
||||
}
|
||||
public WhitelistPlugin(Whitelist allowedNavigations, Whitelist allowedIntents, Whitelist allowedRequests) {
|
||||
if (allowedRequests == null) {
|
||||
allowedRequests = new Whitelist();
|
||||
allowedRequests.addWhiteListEntry("file:///*", false);
|
||||
allowedRequests.addWhiteListEntry("data:*", false);
|
||||
}
|
||||
this.allowedNavigations = allowedNavigations;
|
||||
this.allowedIntents = allowedIntents;
|
||||
this.allowedRequests = allowedRequests;
|
||||
}
|
||||
@Override
|
||||
public void pluginInitialize() {
|
||||
if (allowedNavigations == null) {
|
||||
allowedNavigations = new Whitelist();
|
||||
allowedIntents = new Whitelist();
|
||||
allowedRequests = new Whitelist();
|
||||
new CustomConfigXmlParser().parse(webView.getContext());
|
||||
}
|
||||
}
|
||||
|
||||
private class CustomConfigXmlParser extends ConfigXmlParser {
|
||||
@Override
|
||||
public void handleStartTag(XmlPullParser xml) {
|
||||
String strNode = xml.getName();
|
||||
if (strNode.equals("content")) {
|
||||
String startPage = xml.getAttributeValue(null, "src");
|
||||
allowedNavigations.addWhiteListEntry(startPage, false);
|
||||
} else if (strNode.equals("allow-navigation")) {
|
||||
String origin = xml.getAttributeValue(null, "href");
|
||||
if ("*".equals(origin)) {
|
||||
allowedNavigations.addWhiteListEntry("http://*/*", false);
|
||||
allowedNavigations.addWhiteListEntry("https://*/*", false);
|
||||
allowedNavigations.addWhiteListEntry("data:*", false);
|
||||
} else {
|
||||
allowedNavigations.addWhiteListEntry(origin, false);
|
||||
}
|
||||
} else if (strNode.equals("allow-intent")) {
|
||||
String origin = xml.getAttributeValue(null, "href");
|
||||
allowedIntents.addWhiteListEntry(origin, false);
|
||||
} else if (strNode.equals("access")) {
|
||||
String origin = xml.getAttributeValue(null, "origin");
|
||||
String subdomains = xml.getAttributeValue(null, "subdomains");
|
||||
boolean external = (xml.getAttributeValue(null, "launch-external") != null);
|
||||
if (origin != null) {
|
||||
if (external) {
|
||||
LOG.w(LOG_TAG, "Found <access launch-external> within config.xml. Please use <allow-intent> instead.");
|
||||
allowedIntents.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
|
||||
} else {
|
||||
if ("*".equals(origin)) {
|
||||
allowedRequests.addWhiteListEntry("http://*/*", false);
|
||||
allowedRequests.addWhiteListEntry("https://*/*", false);
|
||||
} else {
|
||||
allowedRequests.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void handleEndTag(XmlPullParser xml) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean shouldAllowNavigation(String url) {
|
||||
if (allowedNavigations.isUrlWhiteListed(url)) {
|
||||
return true;
|
||||
}
|
||||
return null; // Default policy
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean shouldAllowRequest(String url) {
|
||||
if (Boolean.TRUE == shouldAllowNavigation(url)) {
|
||||
return true;
|
||||
}
|
||||
if (allowedRequests.isUrlWhiteListed(url)) {
|
||||
return true;
|
||||
}
|
||||
return null; // Default policy
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean shouldOpenExternalUrl(String url) {
|
||||
if (allowedIntents.isUrlWhiteListed(url)) {
|
||||
return true;
|
||||
}
|
||||
return null; // Default policy
|
||||
}
|
||||
|
||||
public Whitelist getAllowedNavigations() {
|
||||
return allowedNavigations;
|
||||
}
|
||||
|
||||
public void setAllowedNavigations(Whitelist allowedNavigations) {
|
||||
this.allowedNavigations = allowedNavigations;
|
||||
}
|
||||
|
||||
public Whitelist getAllowedIntents() {
|
||||
return allowedIntents;
|
||||
}
|
||||
|
||||
public void setAllowedIntents(Whitelist allowedIntents) {
|
||||
this.allowedIntents = allowedIntents;
|
||||
}
|
||||
|
||||
public Whitelist getAllowedRequests() {
|
||||
return allowedRequests;
|
||||
}
|
||||
|
||||
public void setAllowedRequests(Whitelist allowedRequests) {
|
||||
this.allowedRequests = allowedRequests;
|
||||
}
|
||||
}
|
@ -1,90 +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.
|
||||
*/
|
||||
package org.crosswalk.engine;
|
||||
|
||||
import org.apache.cordova.ICordovaClientCertRequest;
|
||||
import org.xwalk.core.ClientCertRequest;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.Principal;
|
||||
import java.security.PrivateKey;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class XWalkCordovaClientCertRequest implements ICordovaClientCertRequest {
|
||||
|
||||
private final ClientCertRequest request;
|
||||
|
||||
public XWalkCordovaClientCertRequest(ClientCertRequest request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel this request
|
||||
*/
|
||||
public void cancel() {
|
||||
request.cancel();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the host name of the server requesting the certificate.
|
||||
*/
|
||||
public String getHost() {
|
||||
return request.getHost();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the acceptable types of asymmetric keys (can be null).
|
||||
*/
|
||||
public String[] getKeyTypes() {
|
||||
return request.getKeyTypes();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the port number of the server requesting the certificate.
|
||||
*/
|
||||
public int getPort() {
|
||||
return request.getPort();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the acceptable certificate issuers for the certificate matching the private
|
||||
* key (can be null).
|
||||
*/
|
||||
public Principal[] getPrincipals() {
|
||||
return request.getPrincipals();
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore the request for now. Do not remember user's choice.
|
||||
*/
|
||||
public void ignore() {
|
||||
request.ignore();
|
||||
}
|
||||
|
||||
/*
|
||||
* Proceed with the specified private key and client certificate chain. Remember the user's
|
||||
* positive choice and use it for future requests.
|
||||
*
|
||||
* @param privateKey The privateKey
|
||||
* @param chain The certificate chain
|
||||
*/
|
||||
public void proceed(PrivateKey privateKey, X509Certificate[] chain) {
|
||||
request.proceed(privateKey, Arrays.asList(chain));
|
||||
}
|
||||
}
|
@ -1,53 +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.
|
||||
*/
|
||||
package org.crosswalk.engine;
|
||||
|
||||
import org.apache.cordova.ICordovaCookieManager;
|
||||
import org.xwalk.core.XWalkCookieManager;
|
||||
|
||||
class XWalkCordovaCookieManager implements ICordovaCookieManager {
|
||||
|
||||
protected XWalkCookieManager cookieManager = null;
|
||||
|
||||
public XWalkCordovaCookieManager() {
|
||||
cookieManager = new XWalkCookieManager();
|
||||
}
|
||||
|
||||
public void setCookiesEnabled(boolean accept) {
|
||||
cookieManager.setAcceptCookie(accept);
|
||||
}
|
||||
|
||||
public void setCookie(final String url, final String value) {
|
||||
cookieManager.setCookie(url, value);
|
||||
}
|
||||
|
||||
public String getCookie(final String url) {
|
||||
return cookieManager.getCookie(url);
|
||||
}
|
||||
|
||||
public void clearCookies() {
|
||||
cookieManager.removeAllCookie();
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
cookieManager.flushCookieStore();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,52 +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.
|
||||
*/
|
||||
package org.crosswalk.engine;
|
||||
|
||||
import org.apache.cordova.ICordovaHttpAuthHandler;
|
||||
import org.xwalk.core.XWalkHttpAuthHandler;
|
||||
|
||||
/**
|
||||
* Specifies interface for HTTP auth handler object which is used to handle auth requests and
|
||||
* specifying user credentials.
|
||||
*/
|
||||
public class XWalkCordovaHttpAuthHandler implements ICordovaHttpAuthHandler {
|
||||
|
||||
private final XWalkHttpAuthHandler handler;
|
||||
|
||||
public XWalkCordovaHttpAuthHandler(XWalkHttpAuthHandler handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the XWalkView to cancel the authentication request.
|
||||
*/
|
||||
public void cancel() {
|
||||
handler.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the XWalkView to proceed with the authentication with the given credentials.
|
||||
*
|
||||
* @param username
|
||||
* @param password
|
||||
*/
|
||||
public void proceed(String username, String password) {
|
||||
handler.proceed(username, password);
|
||||
}
|
||||
}
|
@ -1,159 +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.
|
||||
*/
|
||||
package org.crosswalk.engine;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.net.http.SslError;
|
||||
import android.webkit.ValueCallback;
|
||||
import android.webkit.WebResourceResponse;
|
||||
|
||||
import org.apache.cordova.CordovaResourceApi;
|
||||
import org.apache.cordova.CordovaResourceApi.OpenForReadResult;
|
||||
import org.apache.cordova.LOG;
|
||||
import org.apache.cordova.PluginManager;
|
||||
import org.xwalk.core.ClientCertRequest;
|
||||
import org.xwalk.core.XWalkHttpAuthHandler;
|
||||
import org.xwalk.core.XWalkResourceClient;
|
||||
import org.xwalk.core.XWalkView;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
||||
public class XWalkCordovaResourceClient extends XWalkResourceClient {
|
||||
|
||||
private static final String TAG = "XWalkCordovaResourceClient";
|
||||
protected XWalkWebViewEngine parentEngine;
|
||||
|
||||
public XWalkCordovaResourceClient(XWalkWebViewEngine parentEngine) {
|
||||
super(parentEngine.webView);
|
||||
this.parentEngine = parentEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable).
|
||||
* The errorCode parameter corresponds to one of the ERROR_* constants.
|
||||
*
|
||||
* @param view The WebView that is initiating the callback.
|
||||
* @param errorCode The error code corresponding to an ERROR_* value.
|
||||
* @param description A String describing the error.
|
||||
* @param failingUrl The url that failed to load.
|
||||
*/
|
||||
@Override
|
||||
public void onReceivedLoadError(XWalkView view, int errorCode, String description,
|
||||
String failingUrl) {
|
||||
LOG.d(TAG, "CordovaWebViewClient.onReceivedError: Error code=%s Description=%s URL=%s", errorCode, description, failingUrl);
|
||||
|
||||
parentEngine.client.onReceivedError(errorCode, description, failingUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptLoadRequest(XWalkView view, String url) {
|
||||
try {
|
||||
// Check the against the white-list.
|
||||
if (!parentEngine.pluginManager.shouldAllowRequest(url)) {
|
||||
LOG.w(TAG, "URL blocked by whitelist: " + url);
|
||||
// Results in a 404.
|
||||
return new WebResourceResponse("text/plain", "UTF-8", null);
|
||||
}
|
||||
|
||||
CordovaResourceApi resourceApi = parentEngine.resourceApi;
|
||||
Uri origUri = Uri.parse(url);
|
||||
// Allow plugins to intercept WebView requests.
|
||||
Uri remappedUri = resourceApi.remapUri(origUri);
|
||||
|
||||
if (!origUri.equals(remappedUri)) {
|
||||
OpenForReadResult result = resourceApi.openForRead(remappedUri, true);
|
||||
return new WebResourceResponse(result.mimeType, "UTF-8", result.inputStream);
|
||||
}
|
||||
// If we don't need to special-case the request, let the browser load it.
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
if (!(e instanceof FileNotFoundException)) {
|
||||
LOG.e(TAG, "Error occurred while loading a file (returning a 404).", e);
|
||||
}
|
||||
// Results in a 404.
|
||||
return new WebResourceResponse("text/plain", "UTF-8", null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(XWalkView view, String url) {
|
||||
return parentEngine.client.onNavigationAttempt(url);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Notify the host application that an SSL error occurred while loading a
|
||||
* resource. The host application must call either callback.onReceiveValue(true)
|
||||
* or callback.onReceiveValue(false). Note that the decision may be
|
||||
* retained for use in response to future SSL errors. The default behavior
|
||||
* is to pop up a dialog.
|
||||
*/
|
||||
@Override
|
||||
public void onReceivedSslError(XWalkView view, ValueCallback<Boolean> callback, SslError error) {
|
||||
final String packageName = parentEngine.cordova.getActivity().getPackageName();
|
||||
final PackageManager pm = parentEngine.cordova.getActivity().getPackageManager();
|
||||
|
||||
ApplicationInfo appInfo;
|
||||
try {
|
||||
appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
|
||||
if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
|
||||
// debug = true
|
||||
callback.onReceiveValue(true);
|
||||
} else {
|
||||
// debug = false
|
||||
callback.onReceiveValue(false);
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// When it doubt, lock it out!
|
||||
callback.onReceiveValue(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedHttpAuthRequest(XWalkView view, XWalkHttpAuthHandler handler,
|
||||
String host, String realm) {
|
||||
// Check if there is some plugin which can resolve this auth challenge
|
||||
PluginManager pluginManager = parentEngine.pluginManager;
|
||||
if (pluginManager != null && pluginManager.onReceivedHttpAuthRequest(
|
||||
parentEngine.parentWebView,
|
||||
new XWalkCordovaHttpAuthHandler(handler), host, realm)) {
|
||||
parentEngine.client.clearLoadTimeoutTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
// By default handle 401 like we'd normally do!
|
||||
super.onReceivedHttpAuthRequest(view, handler, host, realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedClientCertRequest(XWalkView view, ClientCertRequest request) {
|
||||
// Check if there is some plugin which can resolve this certificate request
|
||||
PluginManager pluginManager = parentEngine.pluginManager;
|
||||
if (pluginManager != null && pluginManager.onReceivedClientCertRequest(
|
||||
parentEngine.parentWebView, new XWalkCordovaClientCertRequest(request))) {
|
||||
parentEngine.client.clearLoadTimeoutTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
super.onReceivedClientCertRequest(view, request);
|
||||
}
|
||||
}
|
@ -1,208 +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.
|
||||
*/
|
||||
package org.crosswalk.engine;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.webkit.ValueCallback;
|
||||
|
||||
import org.apache.cordova.CordovaDialogsHelper;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.LOG;
|
||||
import org.xwalk.core.XWalkJavascriptResult;
|
||||
import org.xwalk.core.XWalkUIClient;
|
||||
import org.xwalk.core.XWalkView;
|
||||
|
||||
import org.crosswalk.engine.XWalkWebViewEngine.PermissionRequestListener;
|
||||
|
||||
public class XWalkCordovaUiClient extends XWalkUIClient {
|
||||
private static final String TAG = "XWalkCordovaUiClient";
|
||||
protected final CordovaDialogsHelper dialogsHelper;
|
||||
protected final XWalkWebViewEngine parentEngine;
|
||||
|
||||
private XWalkFileChooser mFileChooser;
|
||||
private CordovaPlugin mFileChooserResultPlugin;
|
||||
|
||||
private static final int FILECHOOSER_RESULTCODE = 5173;
|
||||
|
||||
public XWalkCordovaUiClient(XWalkWebViewEngine parentEngine) {
|
||||
super(parentEngine.webView);
|
||||
this.parentEngine = parentEngine;
|
||||
dialogsHelper = new CordovaDialogsHelper(parentEngine.webView.getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onJavascriptModalDialog(XWalkView view, JavascriptMessageType type, String url,
|
||||
String message, String defaultValue, XWalkJavascriptResult result) {
|
||||
switch (type) {
|
||||
case JAVASCRIPT_ALERT:
|
||||
return onJsAlert(view, url, message, result);
|
||||
case JAVASCRIPT_CONFIRM:
|
||||
return onJsConfirm(view, url, message, result);
|
||||
case JAVASCRIPT_PROMPT:
|
||||
return onJsPrompt(view, url, message, defaultValue, result);
|
||||
case JAVASCRIPT_BEFOREUNLOAD:
|
||||
// Reuse onJsConfirm to show the dialog.
|
||||
return onJsConfirm(view, url, message, result);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
assert (false);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the client to display a javascript alert dialog.
|
||||
*/
|
||||
public boolean onJsAlert(XWalkView view, String url, String message,
|
||||
final XWalkJavascriptResult result) {
|
||||
dialogsHelper.showAlert(message, new CordovaDialogsHelper.Result() {
|
||||
@Override
|
||||
public void gotResult(boolean success, String value) {
|
||||
if (success) {
|
||||
result.confirm();
|
||||
} else {
|
||||
result.cancel();
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the client to display a confirm dialog to the user.
|
||||
*/
|
||||
public boolean onJsConfirm(XWalkView view, String url, String message,
|
||||
final XWalkJavascriptResult result) {
|
||||
dialogsHelper.showConfirm(message, new CordovaDialogsHelper.Result() {
|
||||
@Override
|
||||
public void gotResult(boolean success, String value) {
|
||||
if (success) {
|
||||
result.confirm();
|
||||
} else {
|
||||
result.cancel();
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the client to display a prompt dialog to the user.
|
||||
* If the client returns true, WebView will assume that the client will
|
||||
* handle the prompt dialog and call the appropriate JsPromptResult method.
|
||||
* <p/>
|
||||
* Since we are hacking prompts for our own purposes, we should not be using them for
|
||||
* this purpose, perhaps we should hack console.log to do this instead!
|
||||
*/
|
||||
public boolean onJsPrompt(XWalkView view, String origin, String message, String defaultValue,
|
||||
final XWalkJavascriptResult result) {
|
||||
// Unlike the @JavascriptInterface bridge, this method is always called on the UI thread.
|
||||
String handledRet = parentEngine.bridge.promptOnJsPrompt(origin, message, defaultValue);
|
||||
if (handledRet != null) {
|
||||
result.confirmWithResult(handledRet);
|
||||
} else {
|
||||
dialogsHelper.showPrompt(message, defaultValue, new CordovaDialogsHelper.Result() {
|
||||
@Override
|
||||
public void gotResult(boolean success, String value) {
|
||||
if (success) {
|
||||
result.confirmWithResult(value);
|
||||
} else {
|
||||
result.cancel();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the host application that a page has started loading.
|
||||
* This method is called once for each main frame load so a page with iframes or framesets will call onPageLoadStarted
|
||||
* one time for the main frame. This also means that onPageLoadStarted will not be called when the contents of an
|
||||
* embedded frame changes, i.e. clicking a link whose target is an iframe.
|
||||
*
|
||||
* @param view The webView initiating the callback.
|
||||
* @param url The url of the page.
|
||||
*/
|
||||
@Override
|
||||
public void onPageLoadStarted(XWalkView view, String url) {
|
||||
LOG.d(TAG, "onPageLoadStarted(" + url + ")");
|
||||
if (view.getUrl() != null) {
|
||||
// Flush stale messages.
|
||||
parentEngine.client.onPageStarted(url);
|
||||
parentEngine.bridge.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the host application that a page has stopped loading.
|
||||
* This method is called only for main frame. When onPageLoadStopped() is called, the rendering picture may not be updated yet.
|
||||
*
|
||||
* @param view The webView initiating the callback.
|
||||
* @param url The url of the page.
|
||||
* @param status The load status of the webView, can be FINISHED, CANCELLED or FAILED.
|
||||
*/
|
||||
@Override
|
||||
public void onPageLoadStopped(XWalkView view, String url, LoadStatus status) {
|
||||
LOG.d(TAG, "onPageLoadStopped(" + url + ")");
|
||||
if (status == LoadStatus.FINISHED) {
|
||||
parentEngine.client.onPageFinishedLoading(url);
|
||||
} else if (status == LoadStatus.FAILED) {
|
||||
// TODO: Should this call parentEngine.client.onReceivedError()?
|
||||
// Right now we call this from ResourceClient, but maybe that is just for sub-resources?
|
||||
}
|
||||
}
|
||||
|
||||
// File Chooser
|
||||
@Override
|
||||
public void openFileChooser(XWalkView view, final ValueCallback<Uri> uploadFile,
|
||||
final String acceptType, final String capture) {
|
||||
if (mFileChooser == null) {
|
||||
mFileChooser = new XWalkFileChooser(parentEngine.cordova.getActivity());
|
||||
mFileChooserResultPlugin = new CordovaPlugin() {
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
mFileChooser.onActivityResult(requestCode, resultCode, intent);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
PermissionRequestListener listener = new PermissionRequestListener() {
|
||||
@Override
|
||||
public void onRequestPermissionResult(int requestCode, String[] permissions,
|
||||
int[] grantResults) {
|
||||
for (int i = 0; i < permissions.length; ++i) {
|
||||
Log.d(TAG, "permission:" + permissions[i] + " result:" + grantResults[i]);
|
||||
}
|
||||
parentEngine.cordova.setActivityResultCallback(mFileChooserResultPlugin);
|
||||
mFileChooser.showFileChooser(uploadFile, acceptType, capture);
|
||||
}
|
||||
};
|
||||
|
||||
if (!parentEngine.requestPermissionsForFileChooser(listener)) {
|
||||
parentEngine.cordova.setActivityResultCallback(mFileChooserResultPlugin);
|
||||
mFileChooser.showFileChooser(uploadFile, acceptType, capture);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
package org.crosswalk.engine;
|
||||
|
||||
import org.apache.cordova.CordovaPreferences;
|
||||
import org.xwalk.core.XWalkPreferences;
|
||||
import org.xwalk.core.XWalkResourceClient;
|
||||
import org.xwalk.core.XWalkUIClient;
|
||||
import org.xwalk.core.XWalkView;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.CordovaWebViewEngine;
|
||||
|
||||
public class XWalkCordovaView extends XWalkView implements CordovaWebViewEngine.EngineView {
|
||||
|
||||
public static final String TAG = "XWalkCordovaView";
|
||||
|
||||
protected XWalkCordovaResourceClient resourceClient;
|
||||
protected XWalkCordovaUiClient uiClient;
|
||||
protected XWalkWebViewEngine parentEngine;
|
||||
|
||||
private static boolean hasSetStaticPref;
|
||||
// This needs to run before the super's constructor.
|
||||
private static Context setGlobalPrefs(Context context, CordovaPreferences preferences) {
|
||||
if (!hasSetStaticPref) {
|
||||
hasSetStaticPref = true;
|
||||
ApplicationInfo ai = null;
|
||||
try {
|
||||
ai = context.getPackageManager().getApplicationInfo(context.getApplicationContext().getPackageName(), PackageManager.GET_META_DATA);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
boolean prefAnimatable = preferences == null ? false : preferences.getBoolean("CrosswalkAnimatable", false);
|
||||
boolean manifestAnimatable = ai.metaData == null ? false : ai.metaData.getBoolean("CrosswalkAnimatable");
|
||||
// Selects between a TextureView (obeys framework transforms applied to view) or a SurfaceView (better performance).
|
||||
XWalkPreferences.setValue(XWalkPreferences.ANIMATABLE_XWALK_VIEW, prefAnimatable || manifestAnimatable);
|
||||
if ((ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
|
||||
XWalkPreferences.setValue(XWalkPreferences.REMOTE_DEBUGGING, true);
|
||||
}
|
||||
XWalkPreferences.setValue(XWalkPreferences.JAVASCRIPT_CAN_OPEN_WINDOW, true);
|
||||
XWalkPreferences.setValue(XWalkPreferences.ALLOW_UNIVERSAL_ACCESS_FROM_FILE, true);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
public XWalkCordovaView(Context context, CordovaPreferences preferences) {
|
||||
super(setGlobalPrefs(context, preferences), (AttributeSet)null);
|
||||
}
|
||||
|
||||
public XWalkCordovaView(Context context, AttributeSet attrs) {
|
||||
super(setGlobalPrefs(context, null), attrs);
|
||||
}
|
||||
|
||||
void init(XWalkWebViewEngine parentEngine) {
|
||||
this.parentEngine = parentEngine;
|
||||
if (resourceClient == null) {
|
||||
setResourceClient(new XWalkCordovaResourceClient(parentEngine));
|
||||
}
|
||||
if (uiClient == null) {
|
||||
setUIClient(new XWalkCordovaUiClient(parentEngine));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourceClient(XWalkResourceClient client) {
|
||||
// XWalk calls this method from its constructor.
|
||||
if (client instanceof XWalkCordovaResourceClient) {
|
||||
this.resourceClient = (XWalkCordovaResourceClient)client;
|
||||
}
|
||||
super.setResourceClient(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUIClient(XWalkUIClient client) {
|
||||
// XWalk calls this method from its constructor.
|
||||
if (client instanceof XWalkCordovaUiClient) {
|
||||
this.uiClient = (XWalkCordovaUiClient)client;
|
||||
}
|
||||
super.setUIClient(client);
|
||||
}
|
||||
|
||||
// Call CordovaInterface to start activity for result to make sure
|
||||
// onActivityResult() callback will be triggered from CordovaActivity correctly.
|
||||
// Todo(leonhsl) How to handle |options|?
|
||||
@Override
|
||||
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
|
||||
parentEngine.cordova.startActivityForResult(new CordovaPlugin() {
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
// Route to XWalkView.
|
||||
Log.i(TAG, "Route onActivityResult() to XWalkView");
|
||||
XWalkCordovaView.this.onActivityResult(requestCode, resultCode, intent);
|
||||
}
|
||||
}, intent, requestCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||
Boolean ret = parentEngine.client.onDispatchKeyEvent(event);
|
||||
if (ret != null) {
|
||||
return ret.booleanValue();
|
||||
}
|
||||
return super.dispatchKeyEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pauseTimers() {
|
||||
// This is called by XWalkViewInternal.onActivityStateChange().
|
||||
// We don't want them paused by default though.
|
||||
}
|
||||
|
||||
public void pauseTimersForReal() {
|
||||
super.pauseTimers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CordovaWebView getCordovaWebView() {
|
||||
return parentEngine == null ? null : parentEngine.getCordovaWebView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBackgroundColor(int color) {
|
||||
if (parentEngine != null && parentEngine.isXWalkReady()) {
|
||||
super.setBackgroundColor(color);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,52 +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.
|
||||
*/
|
||||
package org.crosswalk.engine;
|
||||
|
||||
import android.os.Looper;
|
||||
|
||||
import org.apache.cordova.CordovaBridge;
|
||||
import org.apache.cordova.ExposedJsApi;
|
||||
import org.json.JSONException;
|
||||
import org.xwalk.core.JavascriptInterface;
|
||||
|
||||
class XWalkExposedJsApi implements ExposedJsApi {
|
||||
private final CordovaBridge bridge;
|
||||
|
||||
XWalkExposedJsApi(CordovaBridge bridge) {
|
||||
this.bridge = bridge;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public String exec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException {
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare();
|
||||
}
|
||||
return bridge.jsExec(bridgeSecret, service, action, callbackId, arguments);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void setNativeToJsBridgeMode(int bridgeSecret, int value) throws IllegalAccessException {
|
||||
bridge.jsSetNativeToJsBridgeMode(bridgeSecret, value);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public String retrieveJsMessages(int bridgeSecret, boolean fromOnlineEvent) throws IllegalAccessException {
|
||||
return bridge.jsRetrieveJsMessages(bridgeSecret, fromOnlineEvent);
|
||||
}
|
||||
}
|
@ -1,229 +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.
|
||||
*/
|
||||
|
||||
|
||||
package org.crosswalk.engine;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.Manifest;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.webkit.ValueCallback;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
||||
public class XWalkFileChooser {
|
||||
private static final String IMAGE_TYPE = "image/";
|
||||
private static final String VIDEO_TYPE = "video/";
|
||||
private static final String AUDIO_TYPE = "audio/";
|
||||
private static final String ALL_IMAGE_TYPES = IMAGE_TYPE + "*";
|
||||
private static final String ALL_VIDEO_TYPES = VIDEO_TYPE + "*";
|
||||
private static final String ALL_AUDIO_TYPES = AUDIO_TYPE + "*";
|
||||
private static final String ANY_TYPES = "*/*";
|
||||
private static final String SPLIT_EXPRESSION = ",";
|
||||
private static final String PATH_PREFIX = "file:";
|
||||
private static final String WRITE_EXTERNAL_STORAGE= "android.permission.WRITE_EXTERNAL_STORAGE";
|
||||
|
||||
public static final int INPUT_FILE_REQUEST_CODE = 1;
|
||||
|
||||
private static final String TAG = "XWalkFileChooser";
|
||||
|
||||
private Activity mActivity;
|
||||
private ValueCallback<Uri> mFilePathCallback;
|
||||
private String mCameraPhotoPath;
|
||||
|
||||
public XWalkFileChooser(Activity activity) {
|
||||
mActivity = activity;
|
||||
}
|
||||
|
||||
public boolean showFileChooser(ValueCallback<Uri> uploadFile, String acceptType,
|
||||
String capture) {
|
||||
mFilePathCallback = uploadFile;
|
||||
|
||||
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
if (takePictureIntent.resolveActivity(mActivity.getPackageManager()) != null) {
|
||||
// Create the File where the photo should go
|
||||
File photoFile = createImageFile();
|
||||
// Continue only if the File was successfully created
|
||||
if (photoFile != null) {
|
||||
mCameraPhotoPath = PATH_PREFIX + photoFile.getAbsolutePath();
|
||||
takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
|
||||
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
|
||||
} else {
|
||||
takePictureIntent = null;
|
||||
}
|
||||
}
|
||||
|
||||
Intent camcorder = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
|
||||
Intent soundRecorder = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
|
||||
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
ArrayList<Intent> extraIntents = new ArrayList<Intent>();
|
||||
|
||||
// A single mime type.
|
||||
if (!(acceptType.contains(SPLIT_EXPRESSION) || acceptType.contains(ANY_TYPES))) {
|
||||
if (capture.equals("true")) {
|
||||
if (acceptType.startsWith(IMAGE_TYPE)) {
|
||||
if (takePictureIntent != null) {
|
||||
mActivity.startActivityForResult(takePictureIntent, INPUT_FILE_REQUEST_CODE);
|
||||
Log.d(TAG, "Started taking picture");
|
||||
return true;
|
||||
}
|
||||
} else if (acceptType.startsWith(VIDEO_TYPE)) {
|
||||
mActivity.startActivityForResult(camcorder, INPUT_FILE_REQUEST_CODE);
|
||||
Log.d(TAG, "Started camcorder");
|
||||
return true;
|
||||
} else if (acceptType.startsWith(AUDIO_TYPE)) {
|
||||
mActivity.startActivityForResult(soundRecorder, INPUT_FILE_REQUEST_CODE);
|
||||
Log.d(TAG, "Started sound recorder");
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (acceptType.startsWith(IMAGE_TYPE)) {
|
||||
if (takePictureIntent != null) {
|
||||
extraIntents.add(takePictureIntent);
|
||||
}
|
||||
contentSelectionIntent.setType(ALL_IMAGE_TYPES);
|
||||
} else if (acceptType.startsWith(VIDEO_TYPE)) {
|
||||
extraIntents.add(camcorder);
|
||||
contentSelectionIntent.setType(ALL_VIDEO_TYPES);
|
||||
} else if (acceptType.startsWith(AUDIO_TYPE)) {
|
||||
extraIntents.add(soundRecorder);
|
||||
contentSelectionIntent.setType(ALL_AUDIO_TYPES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't resolve an accept type.
|
||||
if (extraIntents.isEmpty() && canWriteExternalStorage()) {
|
||||
if (takePictureIntent != null) {
|
||||
extraIntents.add(takePictureIntent);
|
||||
}
|
||||
extraIntents.add(camcorder);
|
||||
extraIntents.add(soundRecorder);
|
||||
contentSelectionIntent.setType(ANY_TYPES);
|
||||
}
|
||||
|
||||
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
|
||||
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
|
||||
if (!extraIntents.isEmpty()) {
|
||||
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,
|
||||
extraIntents.toArray(new Intent[] { }));
|
||||
}
|
||||
mActivity.startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE);
|
||||
Log.d(TAG, "Started chooser");
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if(requestCode == INPUT_FILE_REQUEST_CODE && mFilePathCallback != null) {
|
||||
Log.d(TAG, "Activity result: " + resultCode);
|
||||
Uri results = null;
|
||||
|
||||
// Check that the response is a good one
|
||||
if(Activity.RESULT_OK == resultCode) {
|
||||
// In Android M, camera results return an empty Intent rather than null.
|
||||
if(data == null || (data.getAction() == null && data.getData() == null)) {
|
||||
// If there is not data, then we may have taken a photo
|
||||
if(mCameraPhotoPath != null) {
|
||||
results = Uri.parse(mCameraPhotoPath);
|
||||
}
|
||||
} else {
|
||||
String dataString = data.getDataString();
|
||||
if (dataString != null) {
|
||||
results = Uri.parse(dataString);
|
||||
}
|
||||
deleteImageFile();
|
||||
}
|
||||
} else if (Activity.RESULT_CANCELED == resultCode) {
|
||||
deleteImageFile();
|
||||
}
|
||||
|
||||
if (results != null) {
|
||||
Log.d(TAG, "Received file: " + results.toString());
|
||||
}
|
||||
mFilePathCallback.onReceiveValue(results);
|
||||
mFilePathCallback = null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canWriteExternalStorage() {
|
||||
try {
|
||||
PackageManager packageManager = mActivity.getPackageManager();
|
||||
PackageInfo packageInfo = packageManager.getPackageInfo(
|
||||
mActivity.getPackageName(), PackageManager.GET_PERMISSIONS);
|
||||
return Arrays.asList(packageInfo.requestedPermissions).contains(WRITE_EXTERNAL_STORAGE);
|
||||
} catch (NameNotFoundException e) {
|
||||
return false;
|
||||
} catch (NullPointerException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private File createImageFile() {
|
||||
// FIXME: If the external storage state is not "MEDIA_MOUNTED", we need to get
|
||||
// other volume paths by "getVolumePaths()" when it was exposed.
|
||||
String state = Environment.getExternalStorageState();
|
||||
if (!state.equals(Environment.MEDIA_MOUNTED)) {
|
||||
Log.e(TAG, "External storage is not mounted.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create an image file name
|
||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
||||
String imageFileName = "JPEG_" + timeStamp + "_";
|
||||
File storageDir = Environment.getExternalStoragePublicDirectory(
|
||||
Environment.DIRECTORY_PICTURES);
|
||||
if (!storageDir.exists()) {
|
||||
storageDir.mkdirs();
|
||||
}
|
||||
|
||||
try {
|
||||
File file = File.createTempFile(imageFileName, ".jpg", storageDir);
|
||||
Log.d(TAG, "Created image file: " + file.getAbsolutePath());
|
||||
return file;
|
||||
} catch (IOException e) {
|
||||
// Error occurred while creating the File
|
||||
Log.e(TAG, "Unable to create Image File, " +
|
||||
"please make sure permission 'WRITE_EXTERNAL_STORAGE' was added.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean deleteImageFile() {
|
||||
if (mCameraPhotoPath == null || !mCameraPhotoPath.contains(PATH_PREFIX)) {
|
||||
return false;
|
||||
}
|
||||
String filePath = mCameraPhotoPath.split(PATH_PREFIX)[1];
|
||||
boolean result = new File(filePath).delete();
|
||||
Log.d(TAG, "Delete image file: " + filePath + " result: " + result);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,366 +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.
|
||||
*/
|
||||
|
||||
package org.crosswalk.engine;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.AssetManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.Manifest;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.webkit.ValueCallback;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.apache.cordova.CordovaBridge;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CordovaPreferences;
|
||||
import org.apache.cordova.CordovaResourceApi;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.CordovaWebViewEngine;
|
||||
import org.apache.cordova.ICordovaCookieManager;
|
||||
import org.apache.cordova.NativeToJsMessageQueue;
|
||||
import org.apache.cordova.PluginEntry;
|
||||
import org.apache.cordova.PluginManager;
|
||||
import org.xwalk.core.XWalkActivityDelegate;
|
||||
import org.xwalk.core.XWalkNavigationHistory;
|
||||
import org.xwalk.core.XWalkView;
|
||||
import org.xwalk.core.XWalkGetBitmapCallback;
|
||||
|
||||
/**
|
||||
* Glue class between CordovaWebView (main Cordova logic) and XWalkCordovaView (the actual View).
|
||||
*/
|
||||
public class XWalkWebViewEngine implements CordovaWebViewEngine {
|
||||
|
||||
public static final String TAG = "XWalkWebViewEngine";
|
||||
public static final String XWALK_USER_AGENT = "xwalkUserAgent";
|
||||
public static final String XWALK_Z_ORDER_ON_TOP = "xwalkZOrderOnTop";
|
||||
|
||||
private static final String XWALK_EXTENSIONS_FOLDER = "xwalk-extensions";
|
||||
|
||||
private static final int PERMISSION_REQUEST_CODE = 100;
|
||||
|
||||
protected final XWalkCordovaView webView;
|
||||
protected XWalkCordovaCookieManager cookieManager;
|
||||
protected CordovaBridge bridge;
|
||||
protected CordovaWebViewEngine.Client client;
|
||||
protected CordovaWebView parentWebView;
|
||||
protected CordovaInterface cordova;
|
||||
protected PluginManager pluginManager;
|
||||
protected CordovaResourceApi resourceApi;
|
||||
protected NativeToJsMessageQueue nativeToJsMessageQueue;
|
||||
protected XWalkActivityDelegate activityDelegate;
|
||||
protected String startUrl;
|
||||
protected CordovaPreferences preferences;
|
||||
|
||||
/** Used when created via reflection. */
|
||||
public XWalkWebViewEngine(Context context, CordovaPreferences preferences) {
|
||||
this.preferences = preferences;
|
||||
Runnable cancelCommand = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cordova.getActivity().finish();
|
||||
}
|
||||
};
|
||||
Runnable completeCommand = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cookieManager = new XWalkCordovaCookieManager();
|
||||
|
||||
initWebViewSettings();
|
||||
exposeJsInterface(webView, bridge);
|
||||
loadExtensions();
|
||||
|
||||
CordovaPlugin notifPlugin = new CordovaPlugin() {
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
Log.i(TAG, "notifPlugin route onNewIntent() to XWalkView: " + intent.toString());
|
||||
XWalkWebViewEngine.this.webView.onNewIntent(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onMessage(String id, Object data) {
|
||||
if (id.equals("captureXWalkBitmap")) {
|
||||
// Capture bitmap on UI thread.
|
||||
XWalkWebViewEngine.this.cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
XWalkWebViewEngine.this.webView.captureBitmapAsync(
|
||||
new XWalkGetBitmapCallback() {
|
||||
@Override
|
||||
public void onFinishGetBitmap(Bitmap bitmap,
|
||||
int response) {
|
||||
pluginManager.postMessage(
|
||||
"onGotXWalkBitmap", bitmap);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
pluginManager.addService(new PluginEntry("XWalkNotif", notifPlugin));
|
||||
|
||||
// Send the massage of xwalk's ready to plugin.
|
||||
if (pluginManager != null) {
|
||||
pluginManager.postMessage("onXWalkReady", this);
|
||||
}
|
||||
|
||||
if (startUrl != null) {
|
||||
webView.load(startUrl, null);
|
||||
}
|
||||
}
|
||||
};
|
||||
activityDelegate = new XWalkActivityDelegate((Activity) context, cancelCommand, completeCommand);
|
||||
|
||||
webView = new XWalkCordovaView(context, preferences);
|
||||
}
|
||||
|
||||
// Use two-phase init so that the control will work with XML layouts.
|
||||
|
||||
@Override
|
||||
public void init(CordovaWebView parentWebView, CordovaInterface cordova, CordovaWebViewEngine.Client client,
|
||||
CordovaResourceApi resourceApi, PluginManager pluginManager,
|
||||
NativeToJsMessageQueue nativeToJsMessageQueue) {
|
||||
if (this.cordova != null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
this.parentWebView = parentWebView;
|
||||
this.cordova = cordova;
|
||||
this.client = client;
|
||||
this.resourceApi = resourceApi;
|
||||
this.pluginManager = pluginManager;
|
||||
this.nativeToJsMessageQueue = nativeToJsMessageQueue;
|
||||
|
||||
CordovaPlugin activityDelegatePlugin = new CordovaPlugin() {
|
||||
@Override
|
||||
public void onResume(boolean multitasking) {
|
||||
activityDelegate.onResume();
|
||||
}
|
||||
};
|
||||
pluginManager.addService(new PluginEntry("XWalkActivityDelegate", activityDelegatePlugin));
|
||||
|
||||
webView.init(this);
|
||||
|
||||
nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode(
|
||||
new NativeToJsMessageQueue.OnlineEventsBridgeMode.OnlineEventsBridgeModeDelegate() {
|
||||
@Override
|
||||
public void setNetworkAvailable(boolean value) {
|
||||
webView.setNetworkAvailable(value);
|
||||
}
|
||||
@Override
|
||||
public void runOnUiThread(Runnable r) {
|
||||
XWalkWebViewEngine.this.cordova.getActivity().runOnUiThread(r);
|
||||
}
|
||||
}));
|
||||
nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.EvalBridgeMode(this, cordova));
|
||||
bridge = new CordovaBridge(pluginManager, nativeToJsMessageQueue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CordovaWebView getCordovaWebView() {
|
||||
return parentWebView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView() {
|
||||
return webView;
|
||||
}
|
||||
|
||||
private void initWebViewSettings() {
|
||||
webView.setVerticalScrollBarEnabled(false);
|
||||
|
||||
boolean zOrderOnTop = preferences == null ? false : preferences.getBoolean(XWALK_Z_ORDER_ON_TOP, false);
|
||||
webView.setZOrderOnTop(zOrderOnTop);
|
||||
|
||||
// Set xwalk webview settings by Cordova preferences.
|
||||
String xwalkUserAgent = preferences == null ? "" : preferences.getString(XWALK_USER_AGENT, "");
|
||||
if (!xwalkUserAgent.isEmpty()) {
|
||||
webView.setUserAgentString(xwalkUserAgent);
|
||||
}
|
||||
|
||||
String appendUserAgent = preferences.getString("AppendUserAgent", "");
|
||||
if (!appendUserAgent.isEmpty()) {
|
||||
webView.setUserAgentString(webView.getUserAgentString() + " " + appendUserAgent);
|
||||
}
|
||||
|
||||
if (preferences.contains("BackgroundColor")) {
|
||||
int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK);
|
||||
webView.setBackgroundColor(backgroundColor);
|
||||
}
|
||||
}
|
||||
|
||||
private static void exposeJsInterface(XWalkView webView, CordovaBridge bridge) {
|
||||
XWalkExposedJsApi exposedJsApi = new XWalkExposedJsApi(bridge);
|
||||
webView.addJavascriptInterface(exposedJsApi, "_cordovaNative");
|
||||
}
|
||||
|
||||
private void loadExtensions() {
|
||||
AssetManager assetManager = cordova.getActivity().getAssets();
|
||||
String[] extList;
|
||||
try {
|
||||
Log.i(TAG, "Iterate assets/xwalk-extensions folder");
|
||||
extList = assetManager.list(XWALK_EXTENSIONS_FOLDER);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Failed to iterate assets/xwalk-extensions folder");
|
||||
return;
|
||||
}
|
||||
|
||||
for (String path : extList) {
|
||||
// Load the extension.
|
||||
Log.i(TAG, "Start to load extension: " + path);
|
||||
webView.getExtensionManager().loadExtension(XWALK_EXTENSIONS_FOLDER + File.separator + path);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canGoBack() {
|
||||
if (!activityDelegate.isXWalkReady()) return false;
|
||||
return this.webView.getNavigationHistory().canGoBack();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goBack() {
|
||||
if (this.webView.getNavigationHistory().canGoBack()) {
|
||||
this.webView.getNavigationHistory().navigate(XWalkNavigationHistory.Direction.BACKWARD, 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPaused(boolean value) {
|
||||
if (!activityDelegate.isXWalkReady()) return;
|
||||
if (value) {
|
||||
// TODO: I think this has been fixed upstream and we don't need to override pauseTimers() anymore.
|
||||
webView.pauseTimersForReal();
|
||||
} else {
|
||||
webView.resumeTimers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (!activityDelegate.isXWalkReady()) return;
|
||||
webView.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearHistory() {
|
||||
if (!activityDelegate.isXWalkReady()) return;
|
||||
this.webView.getNavigationHistory().clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopLoading() {
|
||||
if (!activityDelegate.isXWalkReady()) return;
|
||||
this.webView.stopLoading();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCache() {
|
||||
if (!activityDelegate.isXWalkReady()) return;
|
||||
webView.clearCache(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() {
|
||||
if (!activityDelegate.isXWalkReady()) return null;
|
||||
return this.webView.getUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICordovaCookieManager getCookieManager() {
|
||||
return cookieManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadUrl(String url, boolean clearNavigationStack) {
|
||||
if (!activityDelegate.isXWalkReady()) {
|
||||
startUrl = url;
|
||||
return;
|
||||
}
|
||||
webView.load(url, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* This API is used in Cordova-Android 6.0.0 override from
|
||||
*
|
||||
* CordovaWebViewEngine.java
|
||||
* @since Cordova 6.0
|
||||
*/
|
||||
public void evaluateJavascript(String js, ValueCallback<String> callback) {
|
||||
webView.evaluateJavascript(js, callback);
|
||||
}
|
||||
|
||||
public boolean isXWalkReady() {
|
||||
return activityDelegate.isXWalkReady();
|
||||
}
|
||||
|
||||
public interface PermissionRequestListener {
|
||||
public void onRequestPermissionResult(int requestCode, String[] permissions,
|
||||
int[] grantResults);
|
||||
}
|
||||
|
||||
public boolean requestPermissionsForFileChooser(final PermissionRequestListener listener) {
|
||||
ArrayList<String> dangerous_permissions = new ArrayList<String>();
|
||||
try {
|
||||
PackageManager packageManager = cordova.getActivity().getPackageManager();
|
||||
PackageInfo packageInfo = packageManager.getPackageInfo(
|
||||
cordova.getActivity().getPackageName(), PackageManager.GET_PERMISSIONS);
|
||||
for (String permission : packageInfo.requestedPermissions) {
|
||||
if (permission.equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
|| permission.equals(Manifest.permission.CAMERA)) {
|
||||
dangerous_permissions.add(permission);
|
||||
}
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
}
|
||||
|
||||
if (dangerous_permissions.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CordovaPlugin permissionRequestPlugin = new CordovaPlugin() {
|
||||
@Override
|
||||
public void onRequestPermissionResult(int requestCode, String[] permissions,
|
||||
int[] grantResults) {
|
||||
if (requestCode != PERMISSION_REQUEST_CODE) return;
|
||||
listener.onRequestPermissionResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
};
|
||||
try {
|
||||
cordova.requestPermissions(permissionRequestPlugin, PERMISSION_REQUEST_CODE,
|
||||
dangerous_permissions.toArray(new String[dangerous_permissions.size()]));
|
||||
} catch (NoSuchMethodError e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,41 +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.
|
||||
*/
|
||||
|
||||
package walljm.dynamicbible;
|
||||
|
||||
import android.os.Bundle;
|
||||
import org.apache.cordova.*;
|
||||
|
||||
public class MainActivity extends CordovaActivity
|
||||
{
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// enable Cordova apps to be started in the background
|
||||
Bundle extras = getIntent().getExtras();
|
||||
if (extras != null && extras.getBoolean("cdvStartInBackground", false)) {
|
||||
moveTaskToBack(true);
|
||||
}
|
||||
|
||||
// Set by <content src="index.html" /> in config.xml
|
||||
loadUrl(launchUrl);
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ import { StrongsModal } from '../components/strongs-modal/strongs-modal';
|
||||
import { ErrorMessage } from '../components/error-message/error-message';
|
||||
import { VersePickerModal } from '../components/verse-picker/verse-picker';
|
||||
|
||||
import {AngularFireModule } from 'angularfire2';
|
||||
import { AngularFireModule } from 'angularfire2';
|
||||
import { AngularFireAuthModule } from 'angularfire2/auth';
|
||||
import { AngularFireDatabaseModule } from 'angularfire2/database';
|
||||
|
||||
|
@ -397,7 +397,7 @@ export class Reference
|
||||
{
|
||||
return this.bookName(42);
|
||||
}
|
||||
// must come before the Gospel of John
|
||||
// must come before the Gospel of John
|
||||
if (fbook.search(/\b(1|i|1st|first)\s*(john|jn|jo)\b/i) !== -1)
|
||||
{
|
||||
return this.bookName(62);
|
||||
@ -410,7 +410,7 @@ export class Reference
|
||||
{
|
||||
return this.bookName(64);
|
||||
}
|
||||
// 1, 2, and 3rd John need to come before this one, else this will catch those book references.
|
||||
// 1, 2, and 3rd John need to come before this one, else this will catch those book references.
|
||||
if (fbook.search(/\b(john|jn|jhn)\b/i) !== -1)
|
||||
{
|
||||
return this.bookName(43);
|
||||
@ -494,7 +494,7 @@ export class Reference
|
||||
if (fbook.search(/\b(revelation|rev|re|revelations|rv)\b/i) !== -1)
|
||||
{
|
||||
return this.bookName(66);
|
||||
}
|
||||
}
|
||||
|
||||
return this.bookName(0);
|
||||
}
|
||||
@ -533,7 +533,7 @@ export class Reference
|
||||
|
||||
return ref.concat(section.end.verse);
|
||||
}
|
||||
|
||||
|
||||
public static Books: Array<Book> = [
|
||||
{
|
||||
name: 'Unkown',
|
||||
@ -1070,8 +1070,8 @@ export class Reference
|
||||
long_name: 'Book of Revelations',
|
||||
last_chapter: 22,
|
||||
chapters: [0, 20, 29, 22, 11, 14, 17, 17, 13, 21, 11, 19, 17, 18, 20, 8, 21, 18, 24, 21, 15, 27, 21]
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
public static bookName(booknum: number): Book
|
||||
{
|
||||
@ -1086,15 +1086,15 @@ export type Book = {
|
||||
book_number: number,
|
||||
last_chapter: number,
|
||||
chapters: [number],
|
||||
};
|
||||
};
|
||||
|
||||
export type Section = {
|
||||
start: Location,
|
||||
end: Location,
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
export type Location = {
|
||||
book: Book,
|
||||
chapter: string,
|
||||
verse: string,
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,17 @@
|
||||
</button>
|
||||
</ion-list>
|
||||
|
||||
<ng-template [ngIf]="!profileService.currentUser()">
|
||||
<ion-item>
|
||||
<button ion-button (click)="profileService.authenticate()">Login With Google</button>
|
||||
</ion-item>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="profileService.currentUser()">
|
||||
<ion-item>
|
||||
<button ion-button (click)="profileService.logout()">Logout</button>
|
||||
</ion-item>
|
||||
</ng-template>
|
||||
|
||||
<ion-list-header>Search Settings</ion-list-header>
|
||||
<ion-item>
|
||||
<ion-label>Show Strongs as Modal</ion-label>
|
||||
|
@ -44,7 +44,7 @@ export class ProfileService {
|
||||
, public fbAuth: AngularFireAuth
|
||||
)
|
||||
{
|
||||
console.log("Setting up default user initially");
|
||||
console.log('Setting up default user initially');
|
||||
this.fbAuth.authState.subscribe(state => this.subscribeToRemoteProfile(db, state));
|
||||
let localObserver = this.userObserver().subscribe(
|
||||
user => {
|
||||
@ -66,22 +66,21 @@ export class ProfileService {
|
||||
profile(): User {
|
||||
if (!this.localProfile)
|
||||
{
|
||||
console.log("Initializing default user");
|
||||
console.log('Initializing default user');
|
||||
this.localProfile = ProfileService.createDefaultUser();
|
||||
}
|
||||
console.log("Returning Profile with user: " + this.localProfile);
|
||||
console.log('Returning Profile with user: ' + this.localProfile);
|
||||
return this.localProfile
|
||||
}
|
||||
|
||||
subscribeToRemoteProfile(db: AngularFireDatabase, user: firebase.User)
|
||||
{
|
||||
if (!user) return;
|
||||
console.log("Subscribing to remote settings for user id%s", user.uid);
|
||||
let obj = db.object(
|
||||
'/settings/' + user.uid);
|
||||
console.log('Subscribing to remote settings for user id%s', user.uid);
|
||||
let obj = db.object('/settings/' + user.uid);
|
||||
this.remoteProfile = {
|
||||
ref: obj,
|
||||
stream: obj.valueChanges(),
|
||||
ref: obj as AngularFireObject<User>,
|
||||
stream: obj.valueChanges() as Observable<User>,
|
||||
};
|
||||
this.profile().username = user.displayName;
|
||||
this.save();
|
||||
@ -93,18 +92,18 @@ export class ProfileService {
|
||||
handleRemotePreferenceChange(user: User)
|
||||
{
|
||||
if (user) {
|
||||
console.log("handling remote settings for user %s", user.username);
|
||||
console.log('handling remote settings for user %s', user.username);
|
||||
if (!user.saved_pages) user.saved_pages = [];
|
||||
this.localProfile = user;
|
||||
console.log("Stashed remote changes locally");
|
||||
console.log('Stashed remote changes locally');
|
||||
// We only save the local change here since this is an update from our remote profile.
|
||||
this.localSave();
|
||||
console.log("Saved remote changes locally");
|
||||
console.log('Saved remote changes locally');
|
||||
} else {
|
||||
// No user is there so we should save our local to the remote.
|
||||
console.log("Saving local to remote for user %s", user.username);
|
||||
console.log('Saving local to remote for user %s', user.username);
|
||||
this.save()
|
||||
console.log("Saved remote for user %s", user.username);
|
||||
console.log('Saved remote for user %s', user.username);
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,7 +133,7 @@ export class ProfileService {
|
||||
this.localSave();
|
||||
// If we have a remote profile then save it there too
|
||||
if (this.remoteProfile) {
|
||||
console.log("Saving remote settings profile for remote user id " + this.profile().uid);
|
||||
console.log('Saving remote settings profile for remote user id ' + this.profile().uid);
|
||||
this.remoteProfile.ref.set(this.profile());
|
||||
}
|
||||
}
|
||||
|