Compare commits

..

6 Commits

Author SHA1 Message Date
Ken Naito
1c9e7fc96a fix: backbutton test code (#1307) 2021-08-02 20:25:04 +09:00
Erisu
5e52b7ee77 fix: logging method 2021-08-02 15:15:27 +09:00
Erisu
f3e98c8651 refactor: rename java_unit_tests command & test runner file 2021-08-02 14:30:25 +09:00
Erisu
bbc9bcae14 refactor: java test runner 2021-08-02 14:30:25 +09:00
Erisu
cd49902ca3 fix: add androidx.test:rules library 2021-08-02 14:30:25 +09:00
Erisu
f4a0e1ec78 ci: add Java instrumentation tests 2021-08-02 14:30:25 +09:00
98 changed files with 2599 additions and 8638 deletions

View File

@@ -27,22 +27,21 @@ jobs:
strategy:
matrix:
node-version: [16.x, 18.x]
node-version: [12.x, 14.x, 16.x]
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: set up JDK 17
uses: actions/setup-java@v3
- name: set up JDK 1.8
uses: actions/setup-java@v1
with:
distribution: 'temurin'
java-version: '17'
java-version: 1.8
- name: Environment Information
run: |
@@ -57,8 +56,6 @@ jobs:
env:
CI: true
- name: upload coverage
if: success()
uses: codecov/codecov-action@v3
- uses: codecov/codecov-action@v1
with:
name: ${{ runner.os }} node.js ${{ matrix.node-version }}
fail_ci_if_error: true

2
.npmrc
View File

@@ -1,2 +0,0 @@
registry=https://registry.npmjs.org

View File

@@ -28,44 +28,22 @@
Cordova Android is an Android application library that allows for Cordova-based projects to be built for the Android Platform. Cordova based applications are, at the core, applications written with web technology: HTML, CSS and JavaScript.
[Apache Cordova](https://cordova.apache.org/) is a project of [The Apache Software Foundation (ASF)](https://apache.org/).
[Apache Cordova](https://cordova.apache.org) is a project of The Apache Software Foundation (ASF).
## Requirements
* Java Development Kit (JDK) 11
* [Android SDK](https://developer.android.com/)
* [Node.js](https://nodejs.org)
- Java Development Kit (JDK) 11
- [Android SDK](https://developer.android.com/)
## Create a Cordova project
## Cordova Android Developer Tools
Follow the instructions in the [**Create your first Cordova app**](https://cordova.apache.org/docs/en/latest/guide/cli/index.html) section of [Apache Cordova Docs](https://cordova.apache.org/docs/en/latest/)
Use the [Cordova command-line tool](https://www.npmjs.com/package/cordova) to create projects and install plugins.
To use a **shared framework**, for example in development, link the appropriate cordova-android platform folder path:
## Using Android Studio
```bash
cordova platform add --link /path/to/cordova-android
```
1. Create a project
2. Import it via "Non-Android Studio Project"
## Updating a Cordova project
## Running the Native Tests
When you install a new version of the [`Cordova CLI`](https://www.npmjs.com/package/cordova) that pins a new version of the [`Cordova-Android`](https://www.npmjs.com/package/cordova-android) platform, you can follow these simple upgrade steps within your project:
```bash
cordova platform rm android
cordova platform add android
```
## Debugging in Android Studio
Import project in Android Studio through _File > Open_ and targeting `/path/to/your-cdv-project/platforms/android/`.
## How to Test Repo Development
```bash
npm install
npm test
```
## Further reading
* [Apache Cordova](https://cordova.apache.org/)
The `test/` directory in this project contains an Android test project that can be used to run different kinds of native tests. Check out the [README contained therein](test/README.md) for more details!

View File

@@ -20,84 +20,6 @@
-->
## Release Notes for Cordova (Android)
### 11.0.0 (Jul 04, 2022)
**Breaking:**
* [GH-1441](https://github.com/apache/cordova-android/pull/1441) feat!: **Android** 12 splash screen
* [GH-1427](https://github.com/apache/cordova-android/pull/1427) feat!: API 32 support
* [GH-1410](https://github.com/apache/cordova-android/pull/1410) feat!: API 31 support
* [GH-1444](https://github.com/apache/cordova-android/pull/1444) fix!: set & use `ANDROID_HOME` as default
* [GH-1411](https://github.com/apache/cordova-android/pull/1411) chore!: Drop Node 12 support
**Features:**
* [GH-1448](https://github.com/apache/cordova-android/pull/1448) feat: Update `androidx.appcompat` version
* [GH-1446](https://github.com/apache/cordova-android/pull/1446) feat: Update gradle plugin version
* [GH-1447](https://github.com/apache/cordova-android/pull/1447) feat: Update google services pluging
* [GH-1431](https://github.com/apache/cordova-android/pull/1431) feat: support custom `compileSdk` setting
* [GH-1311](https://github.com/apache/cordova-android/pull/1311) feat: added support for BoM imports
**Fixes:**
* [GH-1455](https://github.com/apache/cordova-android/pull/1455) fix(`prepare`): `destFile` path separator
* [GH-1453](https://github.com/apache/cordova-android/pull/1453) fix: support installing platfrom from local git checkout
* [GH-1449](https://github.com/apache/cordova-android/pull/1449) fix: accept file cookies only if `AndroidInsecureFileModeEnabled`
* [GH-1443](https://github.com/apache/cordova-android/pull/1443) fix: force `hostname` to lowercase
* [GH-1434](https://github.com/apache/cordova-android/pull/1434) fix: restore `checkReqs` in `prepare.js`
* [GH-1154](https://github.com/apache/cordova-android/pull/1154) fix: move `MainActivity.java` to folder that tracks the app package name (widget id)
**Chores, Dependencies & CI:**
* [GH-1451](https://github.com/apache/cordova-android/pull/1451) chore: display warning on deprecated `<splash>` tag usage
* [GH-1430](https://github.com/apache/cordova-android/pull/1430) chore: remove unneeded deprecated annotation
* [GH-1421](https://github.com/apache/cordova-android/pull/1421) chore(npm): bump `@cordova/eslint-config@^4.0.0`
* [GH-1420](https://github.com/apache/cordova-android/pull/1420) chore(npm): bump dependencies
* [GH-1452](https://github.com/apache/cordova-android/pull/1452) dep: bump `jasmine@4.2.1` w/ `package-lock` rebuild
* [GH-1439](https://github.com/apache/cordova-android/pull/1439) ci: update github action workflow
* [GH-1424](https://github.com/apache/cordova-android/pull/1424) ci: Added Node 18 to test matrix
### 10.1.2 (Apr 11, 2022)
**Fixes:**
* [GH-1372](https://github.com/apache/cordova-android/pull/1372) fix(`AndroidManifest`): explicitly define the `activity` attribute `android:exported`
* [GH-1406](https://github.com/apache/cordova-android/pull/1406) fix: detect `JAVA_HOME` with Java 11
* [GH-1401](https://github.com/apache/cordova-android/pull/1401) fix(GH-1391): Reword minimum build tools version to make it more clear what is actually required.
* [GH-1384](https://github.com/apache/cordova-android/pull/1384) fix: escape `strings.xml` app name
**Chores:**
* [GH-1413](https://github.com/apache/cordova-android/pull/1413) chore: update `package-lock` to satisfy `npm audit`
* [GH-1348](https://github.com/apache/cordova-android/pull/1348) chore: `npmrc`
### 10.1.1 (Sep 13, 2021)
**Fixes:**
* [GH-1349](https://github.com/apache/cordova-android/pull/1349) fix(`PluginManager`): `AllowNavigation` default policy to handle scheme & hostname
* [GH-1342](https://github.com/apache/cordova-android/pull/1342) fix(`AllowListPlugin`): Safely handle default allow navigation policy in allow request
* [GH-1332](https://github.com/apache/cordova-android/pull/1332) fix(`PluginManager`): `AllowBridgeAccess` default policy to handle scheme & hostname
### 10.1.0 (Aug 13, 2021)
**Features:**
* [GH-1213](https://github.com/apache/cordova-android/pull/1213) feat: unify `create` default values & stop project name transform
* [GH-1306](https://github.com/apache/cordova-android/pull/1306) feat: bump `ANDROIDX_APP_COMPAT@1.3.1`
* [GH-1303](https://github.com/apache/cordova-android/pull/1303) feat: bump `Google Services Gradle Plugin@4.3.8`
* [GH-1302](https://github.com/apache/cordova-android/pull/1302) feat: bump `kotlin@1.5.21`
* [GH-1298](https://github.com/apache/cordova-android/pull/1298) feat: support `http` w/ `content` `src` fix
**Fixes:**
* [GH-1214](https://github.com/apache/cordova-android/pull/1214) fix: display project name in Android Studio
* [GH-1300](https://github.com/apache/cordova-android/pull/1300) fix: fall back to project root `repositories.gradle`
**Docs:**
* [GH-1308](https://github.com/apache/cordova-android/pull/1308) doc: update `README` about development & testing
### 10.0.1 (Jul 27, 2021)
**Fixes:**

View File

@@ -36,9 +36,6 @@ module.exports = {
// TODO: Extract this as a proper plugin.
modulemapper.clobbers('cordova/plugin/android/app', 'navigator.app');
// Core Splash Screen
modulemapper.clobbers('cordova/plugin/android/splashscreen', 'navigator.splashscreen');
var APP_PLUGIN_NAME = Number(cordova.platformVersion.split('.')[0]) >= 4 ? 'CoreAndroid' : 'App';
// Inject a listener for the backbutton on the document.

View File

@@ -1,33 +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 exec = require('cordova/exec');
var splashscreen = {
show: function () {
console.log('"navigator.splashscreen.show()" is unsupported on Android.');
},
hide: function () {
exec(null, null, 'CordovaSplashScreenPlugin', 'hide', []);
}
};
module.exports = splashscreen;

View File

@@ -18,6 +18,5 @@
under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionName="1.0"
android:versionCode="1">
package="org.apache.cordova" android:versionName="1.0" android:versionCode="1">
</manifest>

View File

@@ -26,8 +26,6 @@ buildscript {
// Android Gradle Plugin (AGP) Build Tools
classpath "com.android.tools.build:gradle:${cordovaConfig.AGP_VERSION}"
}
cdvHelpers.verifyCordovaConfigForBuild()
}
allprojects {
@@ -44,9 +42,7 @@ allprojects {
apply plugin: 'com.android.library'
android {
namespace 'org.apache.cordova'
compileSdkVersion cordovaConfig.COMPILE_SDK_VERSION
compileSdkVersion cordovaConfig.SDK_VERSION
buildToolsVersion cordovaConfig.BUILD_TOOLS_VERSION
compileOptions {
@@ -77,18 +73,11 @@ android {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/NOTICE'
}
publishing {
singleVariant('release') {
withSourcesJar()
}
}
}
dependencies {
api "androidx.appcompat:appcompat:${cordovaConfig.ANDROIDX_APP_COMPAT_VERSION}"
implementation "androidx.appcompat:appcompat:${cordovaConfig.ANDROIDX_APP_COMPAT_VERSION}"
implementation "androidx.webkit:webkit:${cordovaConfig.ANDROIDX_WEBKIT_VERSION}"
implementation "androidx.core:core-splashscreen:${cordovaConfig.ANDROIDX_CORE_SPLASHSCREEN_VERSION}"
}
/**

View File

@@ -1,16 +1,13 @@
{
"MIN_SDK_VERSION": 24,
"SDK_VERSION": 33,
"COMPILE_SDK_VERSION": null,
"GRADLE_VERSION": "8.0.2",
"MIN_BUILD_TOOLS_VERSION": "33.0.2",
"AGP_VERSION": "8.0.0",
"KOTLIN_VERSION": "1.7.21",
"ANDROIDX_APP_COMPAT_VERSION": "1.6.1",
"ANDROIDX_WEBKIT_VERSION": "1.6.0",
"ANDROIDX_CORE_SPLASHSCREEN_VERSION": "1.0.0",
"GRADLE_PLUGIN_GOOGLE_SERVICES_VERSION": "4.3.15",
"MIN_SDK_VERSION": 22,
"SDK_VERSION": 30,
"GRADLE_VERSION": "7.1.1",
"MIN_BUILD_TOOLS_VERSION": "30.0.3",
"AGP_VERSION": "4.2.2",
"KOTLIN_VERSION": "1.5.21",
"ANDROIDX_APP_COMPAT_VERSION": "1.3.0",
"ANDROIDX_WEBKIT_VERSION": "1.4.0",
"GRADLE_PLUGIN_GOOGLE_SERVICES_VERSION": "4.3.8",
"IS_GRADLE_PLUGIN_GOOGLE_SERVICES_ENABLED": false,
"IS_GRADLE_PLUGIN_KOTLIN_ENABLED": false,
"PACKAGE_NAMESPACE": "io.cordova.helloCordova"
"IS_GRADLE_PLUGIN_KOTLIN_ENABLED": false
}

View File

@@ -46,69 +46,73 @@ if (project.hasProperty('signEnabled')) {
}
}
afterEvaluate {
publishing {
publications {
mavenJava(MavenPublication) {
groupId = 'org.apache.cordova'
artifactId = 'framework'
version = getCordovaAndroidVersion()
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
from components.release
publishing {
publications {
mavenJava(MavenPublication) {
groupId = 'org.apache.cordova'
artifactId = 'framework'
version = getCordovaAndroidVersion()
pom {
name = 'Cordova'
description = 'A library to build Cordova-based projects for the Android platform.'
url = 'https://cordova.apache.org'
artifact(sourcesJar)
artifact("$buildDir/outputs/aar/framework-release.aar")
licenses {
license {
name = 'Apache License, Version 2.0'
url = 'https://www.apache.org/licenses/LICENSE-2.0.txt'
}
pom {
name = 'Cordova'
description = 'A library to build Cordova-based projects for the Android platform.'
url = 'https://cordova.apache.org'
licenses {
license {
name = 'Apache License, Version 2.0'
url = 'https://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer {
id = 'stevengill'
name = 'Steve Gill'
}
developer {
id = 'erisu'
name = 'Bryan Ellis'
email = 'erisu@apache.org'
}
developers {
developer {
id = 'stevengill'
name = 'Steve Gill'
}
developer {
id = 'erisu'
name = 'Bryan Ellis'
email = 'erisu@apache.org'
}
}
scm {
connection = 'scm:git:https://github.com/apache/cordova-android.git'
developerConnection = 'scm:git:git@github.com:apache/cordova-android.git'
url = 'https://github.com/apache/cordova-android'
}
scm {
connection = 'scm:git:https://github.com/apache/cordova-android.git'
developerConnection = 'scm:git:git@github.com:apache/cordova-android.git'
url = 'https://github.com/apache/cordova-android'
}
}
}
}
repositories {
maven {
def releasesRepoUrl = 'https://repository.apache.org/content/repositories/releases'
def snapshotsRepoUrl = 'https://repository.apache.org/content/repositories/snapshots'
repositories {
maven {
def releasesRepoUrl = 'https://repository.apache.org/content/repositories/releases'
def snapshotsRepoUrl = 'https://repository.apache.org/content/repositories/snapshots'
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
credentials {
if (project.hasProperty('apacheUsername') && project.hasProperty('apachePassword')) {
username apacheUsername
password apachePassword
}
credentials {
if (project.hasProperty('apacheUsername') && project.hasProperty('apachePassword')) {
username apacheUsername
password apachePassword
}
}
}
}
signing {
if (Boolean.valueOf(cdvEnableSigning)) {
sign publishing.publications.mavenJava
}
signing {
if (Boolean.valueOf(cdvEnableSigning)) {
sign publishing.publications.mavenJava
}
}
}

View File

@@ -64,16 +64,16 @@ String doFindLatestInstalledBuildTools(String minBuildToolsVersionString) {
if (highestBuildToolsVersion == null) {
throw new RuntimeException("""
No installed build tools found. Please install the Android build tools
version ${minBuildToolsVersionString}.
No installed build tools found. Install the Android build tools
version ${minBuildToolsVersionString} or higher.
""".replaceAll(/\s+/, ' ').trim())
}
if (highestBuildToolsVersion.isLowerThan(minBuildToolsVersionString)) {
throw new RuntimeException("""
No usable Android build tools found. Highest ${minBuildToolsVersion.getMajor()}.x installed version is
${highestBuildToolsVersion.getOriginalString()}; Recommended version
is ${minBuildToolsVersionString}.
${highestBuildToolsVersion.getOriginalString()}; minimum version
required is ${minBuildToolsVersionString}.
""".replaceAll(/\s+/, ' ').trim())
}
@@ -83,9 +83,9 @@ String doFindLatestInstalledBuildTools(String minBuildToolsVersionString) {
String getAndroidSdkDir() {
def rootDir = project.rootDir
def androidSdkDir = null
String envVar = System.getenv("ANDROID_HOME")
String envVar = System.getenv("ANDROID_SDK_ROOT")
if (envVar == null) {
envVar = System.getenv("ANDROID_SDK_ROOT")
envVar = System.getenv("ANDROID_HOME")
}
def localProperties = new File(rootDir, 'local.properties')
@@ -125,6 +125,14 @@ def doExtractIntFromManifest(name) {
return new BigInteger(matcher.group(1))
}
def doExtractStringFromManifest(name) {
def manifestFile = file(android.sourceSets.main.manifest.srcFile)
def pattern = Pattern.compile(name + "=\"(\\S+)\"")
def matcher = pattern.matcher(manifestFile.getText())
matcher.find()
return matcher.group(1)
}
def doGetConfigXml() {
def xml = file("src/main/res/xml/config.xml").getText()
// Disable namespace awareness since Cordova doesn't use them properly
@@ -153,9 +161,6 @@ def doApplyCordovaConfigCustomization() {
if (project.hasProperty('cdvSdkVersion')) {
cordovaConfig.SDK_VERSION = Integer.parseInt('' + cdvSdkVersion)
}
if (project.hasProperty('cdvCompileSdkVersion')) {
cordovaConfig.COMPILE_SDK_VERSION = Integer.parseInt('' + cdvCompileSdkVersion)
}
if (project.hasProperty('cdvMaxSdkVersion')) {
cordovaConfig.MAX_SDK_VERSION = Integer.parseInt('' + cdvMaxSdkVersion)
}
@@ -185,12 +190,6 @@ def doApplyCordovaConfigCustomization() {
}
}
def doVerifyCordovaConfigForBuild() {
if (cordovaConfig.COMPILE_SDK_VERSION < cordovaConfig.SDK_VERSION) {
println "The \"compileSdkVersion\" (${cordovaConfig.COMPILE_SDK_VERSION}) should be greater than or equal to the the \"targetSdkVersion\" (${cordovaConfig.SDK_VERSION})."
}
}
// Properties exported here are visible to all plugins.
ext {
def defaultsFilePath = './cdv-gradle-config-defaults.json'
@@ -211,10 +210,6 @@ ext {
def jsonFile = new File(targetConfigFilePath)
cordovaConfig = new groovy.json.JsonSlurper().parseText(jsonFile.text)
if (cordovaConfig.COMPILE_SDK_VERSION == null) {
cordovaConfig.COMPILE_SDK_VERSION = cordovaConfig.SDK_VERSION
}
// Apply Gradle Properties
doApplyCordovaConfigCustomization()
@@ -223,6 +218,7 @@ ext {
privateHelpers.getProjectTarget = { doGetProjectTarget() }
privateHelpers.applyCordovaConfigCustomization = { doApplyCordovaConfigCustomization() }
privateHelpers.extractIntFromManifest = { name -> doExtractIntFromManifest(name) }
privateHelpers.extractStringFromManifest = { name -> doExtractStringFromManifest(name) }
privateHelpers.ensureValueExists = { filePath, props, key -> doEnsureValueExists(filePath, props, key) }
// These helpers can be used by plugins / projects and will not change.
@@ -231,8 +227,6 @@ ext {
cdvHelpers.getConfigXml = { doGetConfigXml() }
// Returns the value for the desired <preference>. Added in 4.1.0.
cdvHelpers.getConfigPreference = { name, defaultValue -> doGetConfigPreference(name, defaultValue) }
// Display warnings if any cordova config is not proper for build.
cdvHelpers.verifyCordovaConfigForBuild = { doVerifyCordovaConfigForBuild() }
}
buildscript {

View File

@@ -82,6 +82,11 @@ public class AllowListPlugin extends CordovaPlugin {
if (strNode.equals("content")) {
String startPage = xml.getAttributeValue(null, "src");
allowedNavigations.addAllowListEntry(startPage, false);
// Allow origin for WebViewAssetLoader
if (!this.prefs.getBoolean("AndroidInsecureFileModeEnabled", false)) {
allowedNavigations.addAllowListEntry("https://" + this.prefs.getString("hostname", "localhost"), false);
}
} else if (strNode.equals("allow-navigation")) {
String origin = xml.getAttributeValue(null, "href");
if ("*".equals(origin)) {
@@ -122,7 +127,7 @@ public class AllowListPlugin extends CordovaPlugin {
@Override
public Boolean shouldAllowRequest(String url) {
return (Boolean.TRUE.equals(this.shouldAllowNavigation(url)) || this.allowedRequests.isUrlAllowListed(url))
return (this.shouldAllowNavigation(url) || this.allowedRequests.isUrlAllowListed(url))
? true
: null; // default policy
}

View File

@@ -51,8 +51,7 @@ public class BuildHelper {
{
try
{
String packageName = ctx.getApplicationInfo().packageName;
Class<?> clazz = Class.forName(packageName + ".BuildConfig");
Class<?> clazz = Class.forName(ctx.getClass().getPackage().getName() + ".BuildConfig");
Field field = clazz.getField(key);
return field.get(null);
} catch (ClassNotFoundException e) {

View File

@@ -34,7 +34,6 @@ public class ConfigXmlParser {
private static String SCHEME_HTTP = "http";
private static String SCHEME_HTTPS = "https";
private static String DEFAULT_HOSTNAME = "localhost";
private static final String DEFAULT_CONTENT_SRC = "index.html";
private String launchUrl;
private String contentSrc;
@@ -77,14 +76,6 @@ public class ConfigXmlParser {
)
);
pluginEntries.add(
new PluginEntry(
SplashScreenPlugin.PLUGIN_NAME,
"org.apache.cordova.SplashScreenPlugin",
true
)
);
parse(action.getResources().getXml(id));
}
@@ -111,18 +102,6 @@ public class ConfigXmlParser {
e.printStackTrace();
}
}
onPostParse();
}
private void onPostParse() {
// After parsing, if contentSrc is still null, it signals
// that <content> tag was completely missing. In this case,
// default it.
// https://github.com/apache/cordova-android/issues/1432
if (contentSrc == null) {
contentSrc = DEFAULT_CONTENT_SRC;
}
}
public void handleStartTag(XmlPullParser xml) {
@@ -153,7 +132,7 @@ public class ConfigXmlParser {
contentSrc = src;
} else {
// Default
contentSrc = DEFAULT_CONTENT_SRC;
contentSrc = "index.html";
}
}
}
@@ -175,7 +154,7 @@ public class ConfigXmlParser {
return "file:///android_asset/www/";
} else {
String scheme = prefs.getString("scheme", SCHEME_HTTPS).toLowerCase();
String hostname = prefs.getString("hostname", DEFAULT_HOSTNAME).toLowerCase();
String hostname = prefs.getString("hostname", DEFAULT_HOSTNAME);
if (!scheme.contentEquals(SCHEME_HTTP) && !scheme.contentEquals(SCHEME_HTTPS)) {
LOG.d(TAG, "The provided scheme \"" + scheme + "\" is not valid. " +

View File

@@ -42,7 +42,6 @@ import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.splashscreen.SplashScreen;
/**
* This class is the main Android activity that represents the Cordova
@@ -99,16 +98,11 @@ public class CordovaActivity extends AppCompatActivity {
protected ArrayList<PluginEntry> pluginEntries;
protected CordovaInterfaceImpl cordovaInterface;
private SplashScreen splashScreen;
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
// Handle the splash screen transition.
splashScreen = SplashScreen.installSplashScreen(this);
// need to activate preferences before super.onCreate to avoid "requestFeature() must be called before adding content" exception
loadConfig();
@@ -131,6 +125,8 @@ public class CordovaActivity extends AppCompatActivity {
// (as was the case in previous cordova versions)
if (!preferences.getBoolean("FullscreenNotImmersive", false)) {
immersiveMode = true;
// The splashscreen plugin needs the flags set before we're focused to prevent
// the nav and title bars from flashing in.
setImmersiveUiVisibility();
} else {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
@@ -157,9 +153,6 @@ public class CordovaActivity extends AppCompatActivity {
}
cordovaInterface.onCordovaInit(appView.getPluginManager());
// Setup the splash screen based on preference settings
cordovaInterface.pluginManager.postMessage("setupSplashScreen", splashScreen);
// Wire the hardware volume controls to control media if desired.
String volumePref = preferences.getString("DefaultVolumeStream", "");
if ("media".equals(volumePref.toLowerCase(Locale.ENGLISH))) {
@@ -533,4 +526,5 @@ public class CordovaActivity extends AppCompatActivity {
}
}
}

View File

@@ -237,6 +237,14 @@ public class CordovaInterfaceImpl implements CordovaInterface {
public boolean hasPermission(String permission)
{
return PackageManager.PERMISSION_GRANTED == activity.checkSelfPermission(permission);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
int result = activity.checkSelfPermission(permission);
return PackageManager.PERMISSION_GRANTED == result;
}
else
{
return true;
}
}
}

View File

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

View File

@@ -19,8 +19,6 @@
package org.apache.cordova;
import org.apache.cordova.BuildHelper;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -32,6 +30,7 @@ import android.content.IntentFilter;
import android.telephony.TelephonyManager;
import android.view.KeyEvent;
import java.lang.reflect.Field;
import java.util.HashMap;
/**
@@ -377,19 +376,35 @@ public class CoreAndroid extends CordovaPlugin {
}
}
/*
/*
* 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!
*
* @deprecated Use {@link BuildHelper#getBuildConfigValue} instead.
*/
@Deprecated
public static Object getBuildConfigValue(Context ctx, String key)
{
LOG.w(TAG, "CoreAndroid.getBuildConfigValue is deprecated and will be removed in a future release. Use BuildHelper.getBuildConfigValue instead.");
return BuildHelper.getBuildConfigValue(ctx, key);
try
{
Class<?> clazz = Class.forName(ctx.getClass().getPackage().getName() + ".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();
} catch (NullPointerException e) {
LOG.d(TAG, "Null Pointer Exception: Let's print a stack trace.");
e.printStackTrace();
}
return null;
}
}

View File

@@ -41,12 +41,6 @@ import android.os.Build;
*/
public class PluginManager {
private static String TAG = "PluginManager";
// @todo same as ConfigXmlParser. Research centralizing ideas, maybe create CordovaConstants
private static String SCHEME_HTTPS = "https";
// @todo same as ConfigXmlParser. Research centralizing ideas, maybe create CordovaConstants
private static String DEFAULT_HOSTNAME = "localhost";
private static final int SLOW_EXEC_WARNING_THRESHOLD = Debug.isDebuggerConnected() ? 60 : 16;
// List of service entries
@@ -339,11 +333,22 @@ public class PluginManager {
public Object postMessage(String id, Object data) {
LOG.d(TAG, "postMessage: " + id);
synchronized (this.pluginMap) {
this.pluginMap.forEach((s, plugin) -> {
if (plugin != null) {
plugin.onMessage(id, data);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
this.pluginMap.forEach((s, plugin) -> {
if (plugin != null) {
plugin.onMessage(id, data);
}
});
} else {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
Object obj = plugin.onMessage(id, data);
if (obj != null) {
return obj;
}
}
}
});
}
}
return ctx.onMessage(id, data);
}
@@ -361,24 +366,6 @@ public class PluginManager {
}
}
/**
* @todo should we move this somewhere public and accessible by all plugins?
* For now, it is placed where it is used and kept private so we can decide later and move without causing a breaking change.
* An ideal location might be in the "ConfigXmlParser" at the time it generates the "launchUrl".
*
* @todo should we be restrictive on the "file://" return? e.g. "file:///android_asset/www/"
* Would be considered as a breaking change if we apply a more granular check.
*/
private String getLaunchUrlPrefix() {
if (!app.getPreferences().getBoolean("AndroidInsecureFileModeEnabled", false)) {
String scheme = app.getPreferences().getString("scheme", SCHEME_HTTPS).toLowerCase();
String hostname = app.getPreferences().getString("hostname", DEFAULT_HOSTNAME).toLowerCase();
return scheme + "://" + hostname + '/';
}
return "file://";
}
/**
* Called when the webview is going to request an external resource.
*
@@ -444,7 +431,7 @@ public class PluginManager {
}
// Default policy:
return url.startsWith(getLaunchUrlPrefix()) || url.startsWith("about:blank");
return url.startsWith("file://") || url.startsWith("about:blank");
}
@@ -465,7 +452,7 @@ public class PluginManager {
}
// Default policy:
return url.startsWith(getLaunchUrlPrefix());
return url.startsWith("file://");
}
/**

View File

@@ -1,170 +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;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.SuppressLint;
import android.os.Handler;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import androidx.annotation.NonNull;
import androidx.core.splashscreen.SplashScreen;
import androidx.core.splashscreen.SplashScreenViewProvider;
import org.json.JSONArray;
import org.json.JSONException;
@SuppressLint("LongLogTag")
public class SplashScreenPlugin extends CordovaPlugin {
static final String PLUGIN_NAME = "CordovaSplashScreenPlugin";
// Default config preference values
private static final boolean DEFAULT_AUTO_HIDE = true;
private static final int DEFAULT_DELAY_TIME = -1;
private static final boolean DEFAULT_FADE = true;
private static final int DEFAULT_FADE_TIME = 500;
// Config preference values
/**
* @param boolean autoHide to auto splash screen (default=true)
*/
private boolean autoHide;
/**
* @param int delayTime in milliseconds (default=-1)
*/
private int delayTime;
/**
* @param int fade to fade out splash screen (default=true)
*/
private boolean isFadeEnabled;
/**
* @param int fadeDuration fade out duration in milliseconds (default=500)
*/
private int fadeDuration;
// Internal variables
/**
* @param boolean keepOnScreen flag to determine if the splash screen remains visible.
*/
private boolean keepOnScreen = true;
@Override
protected void pluginInitialize() {
// Auto Hide & Delay Settings
autoHide = preferences.getBoolean("AutoHideSplashScreen", DEFAULT_AUTO_HIDE);
delayTime = preferences.getInteger("SplashScreenDelay", DEFAULT_DELAY_TIME);
LOG.d(PLUGIN_NAME, "Auto Hide: " + autoHide);
if (delayTime != DEFAULT_DELAY_TIME) {
LOG.d(PLUGIN_NAME, "Delay: " + delayTime + "ms");
}
// Fade & Fade Duration
isFadeEnabled = preferences.getBoolean("FadeSplashScreen", DEFAULT_FADE);
fadeDuration = preferences.getInteger("FadeSplashScreenDuration", DEFAULT_FADE_TIME);
LOG.d(PLUGIN_NAME, "Fade: " + isFadeEnabled);
if (isFadeEnabled) {
LOG.d(PLUGIN_NAME, "Fade Duration: " + fadeDuration + "ms");
}
}
@Override
public boolean execute(
String action,
JSONArray args,
CallbackContext callbackContext
) throws JSONException {
if (action.equals("hide") && autoHide == false) {
/*
* The `.hide()` method can only be triggered if the `splashScreenAutoHide`
* is set to `false`.
*/
keepOnScreen = false;
} else {
return false;
}
callbackContext.success();
return true;
}
@Override
public Object onMessage(String id, Object data) {
switch (id) {
case "setupSplashScreen":
setupSplashScreen((SplashScreen) data);
break;
case "onPageFinished":
attemptCloseOnPageFinished();
break;
}
return null;
}
private void setupSplashScreen(SplashScreen splashScreen) {
// Setup Splash Screen Delay
splashScreen.setKeepOnScreenCondition(() -> keepOnScreen);
// auto hide splash screen when custom delay is defined.
if (autoHide && delayTime != DEFAULT_DELAY_TIME) {
Handler splashScreenDelayHandler = new Handler(cordova.getContext().getMainLooper());
splashScreenDelayHandler.postDelayed(() -> keepOnScreen = false, delayTime);
}
// auto hide splash screen with default delay (-1) delay is controlled by the
// `onPageFinished` message.
// If auto hide is disabled (false), the hiding of the splash screen must be determined &
// triggered by the front-end code with the `navigator.splashscreen.hide()` method.
if (isFadeEnabled) {
// Setup the fade
splashScreen.setOnExitAnimationListener(new SplashScreen.OnExitAnimationListener() {
@Override
public void onSplashScreenExit(@NonNull SplashScreenViewProvider splashScreenViewProvider) {
View splashScreenView = splashScreenViewProvider.getView();
splashScreenView
.animate()
.alpha(0.0f)
.setDuration(fadeDuration)
.setStartDelay(0)
.setInterpolator(new AccelerateInterpolator())
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
splashScreenViewProvider.remove();
}
}).start();
}
});
}
}
private void attemptCloseOnPageFinished() {
if (autoHide && delayTime == DEFAULT_DELAY_TIME) {
keepOnScreen = false;
}
}
}

View File

@@ -19,6 +19,8 @@
package org.apache.cordova.engine;
import android.annotation.TargetApi;
import android.os.Build;
import android.webkit.CookieManager;
import android.webkit.WebView;
@@ -33,12 +35,8 @@ class SystemCookieManager implements ICordovaCookieManager {
webView = webview;
cookieManager = CookieManager.getInstance();
cookieManager.setAcceptThirdPartyCookies(webView, true);
}
@SuppressWarnings("deprecation")
public void setAcceptFileSchemeCookies() {
cookieManager.setAcceptFileSchemeCookies(true);
cookieManager.setAcceptThirdPartyCookies(webView, true);
}
public void setCookiesEnabled(boolean accept) {
@@ -53,6 +51,7 @@ class SystemCookieManager implements ICordovaCookieManager {
return cookieManager.getCookie(url);
}
@SuppressWarnings("deprecation")
public void clearCookies() {
cookieManager.removeAllCookies(null);
}

View File

@@ -71,7 +71,7 @@ public class SystemWebViewClient extends WebViewClient {
this.parentEngine = parentEngine;
WebViewAssetLoader.Builder assetLoaderBuilder = new WebViewAssetLoader.Builder()
.setDomain(parentEngine.preferences.getString("hostname", "localhost").toLowerCase())
.setDomain(parentEngine.preferences.getString("hostname", "localhost"))
.setHttpAllowed(true);
assetLoaderBuilder.addPathHandler("/", path -> {

View File

@@ -165,7 +165,6 @@ public class SystemWebViewEngine implements CordovaWebViewEngine {
LOG.d(TAG, "Enabled insecure file access");
settings.setAllowFileAccess(true);
settings.setAllowUniversalAccessFromFileURLs(true);
cookieManager.setAcceptFileSchemeCookies();
}
settings.setMediaPlaybackRequiresUserGesture(false);
@@ -175,20 +174,9 @@ public class SystemWebViewEngine implements CordovaWebViewEngine {
String databasePath = webView.getContext().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
settings.setDatabaseEnabled(true);
// The default is to use the module's debuggable state to decide if the webview inspecter
// should be enabled. However, users can configure InspectableWebview preference to forcefully enable
// or disable the webview inspecter.
String inspectableWebview = preferences.getString("InspectableWebview", null);
boolean shouldEnableInspector = false;
if (inspectableWebview == null) {
ApplicationInfo appInfo = webView.getContext().getApplicationContext().getApplicationInfo();
shouldEnableInspector = (appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
}
else if ("true".equals(inspectableWebview)) {
shouldEnableInspector = true;
}
if (shouldEnableInspector) {
//Determine whether we're in debug or release mode, and turn on Debugging!
ApplicationInfo appInfo = webView.getContext().getApplicationContext().getApplicationInfo();
if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
enableRemoteDebugging();
}

View File

@@ -17,12 +17,12 @@
under the License.
*/
const os = require('os');
const execa = require('execa');
const events = require('cordova-common').events;
const CordovaError = require('cordova-common').CordovaError;
var os = require('os');
var execa = require('execa');
var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;
const Adb = {};
var Adb = {};
/**
* Lists available/connected devices and emulators
@@ -50,7 +50,7 @@ Adb.devices = async function () {
Adb.install = function (target, packagePath, { replace = false, execOptions = {} } = {}) {
events.emit('verbose', 'Installing apk ' + packagePath + ' on target ' + target + '...');
const args = ['-s', target, 'install'];
var args = ['-s', target, 'install'];
if (replace) args.push('-r');
const opts = { cwd: os.tmpdir(), ...execOptions };
@@ -79,7 +79,7 @@ Adb.uninstall = function (target, packageId) {
Adb.shell = function (target, shellCommand) {
events.emit('verbose', 'Running adb shell command "' + shellCommand + '" on target ' + target + '...');
const args = ['-s', target, 'shell'];
var args = ['-s', target, 'shell'];
shellCommand = shellCommand.split(/\s+/);
return execa('adb', args.concat(shellCommand), { cwd: os.tmpdir() })
.then(({ stdout }) => stdout)

View File

@@ -17,10 +17,10 @@
under the License.
*/
const fs = require('fs');
const xml = require('cordova-common').xmlHelpers;
var fs = require('fs');
var xml = require('cordova-common').xmlHelpers;
const DEFAULT_ORIENTATION = 'default';
var DEFAULT_ORIENTATION = 'default';
/** Wraps an AndroidManifest file */
class AndroidManifest {
@@ -50,8 +50,17 @@ class AndroidManifest {
return this;
}
getPackageId () {
return this.doc.getroot().attrib.package;
}
setPackageId (pkgId) {
this.doc.getroot().attrib.package = pkgId;
return this;
}
getActivity () {
const activity = this.doc.getroot().find('./application/activity');
var activity = this.doc.getroot().find('./application/activity');
return {
getName: function () {
return activity.attrib['android:name'];
@@ -94,7 +103,7 @@ class AndroidManifest {
}
setDebuggable (value) {
const application = this.doc.getroot().find('./application');
var application = this.doc.getroot().find('./application');
if (value) {
application.attrib['android:debuggable'] = 'true';
} else {

View File

@@ -17,16 +17,16 @@
under the License.
*/
const fs = require('fs');
const path = require('path');
const properties_parser = require('properties-parser');
const pluginHandlers = require('./pluginHandlers');
const CordovaGradleConfigParserFactory = require('./config/CordovaGradleConfigParserFactory');
var fs = require('fs');
var path = require('path');
var properties_parser = require('properties-parser');
var AndroidManifest = require('./AndroidManifest');
var pluginHandlers = require('./pluginHandlers');
let projectFileCache = {};
var projectFileCache = {};
function addToPropertyList (projectProperties, key, value) {
let i = 1;
var i = 1;
while (projectProperties.get(key + '.' + i)) { i++; }
projectProperties.set(key + '.' + i, value);
@@ -34,8 +34,8 @@ function addToPropertyList (projectProperties, key, value) {
}
function removeFromPropertyList (projectProperties, key, value) {
let i = 1;
let currentValue;
var i = 1;
var currentValue;
while ((currentValue = projectProperties.get(key + '.' + i))) {
if (currentValue === value) {
while ((currentValue = projectProperties.get(key + '.' + (i + 1)))) {
@@ -51,7 +51,7 @@ function removeFromPropertyList (projectProperties, key, value) {
}
function getRelativeLibraryPath (parentDir, subDir) {
const libraryPath = path.relative(parentDir, subDir);
var libraryPath = path.relative(parentDir, subDir);
return (path.sep === '\\') ? libraryPath.replace(/\\/g, '/') : libraryPath;
}
@@ -63,36 +63,36 @@ class AndroidProject {
this.projectDir = projectDir;
this.platformWww = path.join(this.projectDir, 'platform_www');
this.www = path.join(this.projectDir, 'app/src/main/assets/www');
this.cordovaGradleConfigParser = CordovaGradleConfigParserFactory.create(this.projectDir);
}
/**
* Reads the package name out of the Cordova's Gradle Config file
* Reads the package name out of the Android Manifest file
*
* @param {String} projectDir The absolute path to the directory containing the project
* @return {String} The name of the package
*/
getPackageName () {
return this.cordovaGradleConfigParser.getPackageName();
var manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml');
return new AndroidManifest(manifestPath).getPackageId();
}
getCustomSubprojectRelativeDir (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.
const packageName = this.getPackageName();
const lastDotIndex = packageName.lastIndexOf('.');
const prefix = packageName.substring(lastDotIndex + 1);
const subRelativeDir = path.join(plugin_id, prefix + '-' + path.basename(src));
var packageName = this.getPackageName();
var lastDotIndex = packageName.lastIndexOf('.');
var prefix = packageName.substring(lastDotIndex + 1);
var subRelativeDir = path.join(plugin_id, prefix + '-' + path.basename(src));
return subRelativeDir;
}
addSubProject (parentDir, subDir) {
const parentProjectFile = path.resolve(parentDir, 'project.properties');
const subProjectFile = path.resolve(subDir, 'project.properties');
const parentProperties = this._getPropertiesFile(parentProjectFile);
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var subProjectFile = path.resolve(subDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
// TODO: Setting the target needs to happen only for pre-3.7.0 projects
if (fs.existsSync(subProjectFile)) {
const subProperties = this._getPropertiesFile(subProjectFile);
var subProperties = this._getPropertiesFile(subProjectFile);
subProperties.set('target', parentProperties.get('target'));
subProperties.dirty = true;
this._subProjectDirs[subDir] = true;
@@ -103,37 +103,37 @@ class AndroidProject {
}
removeSubProject (parentDir, subDir) {
const parentProjectFile = path.resolve(parentDir, 'project.properties');
const parentProperties = this._getPropertiesFile(parentProjectFile);
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir));
delete this._subProjectDirs[subDir];
this._dirty = true;
}
addGradleReference (parentDir, subDir) {
const parentProjectFile = path.resolve(parentDir, 'project.properties');
const parentProperties = this._getPropertiesFile(parentProjectFile);
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
addToPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
}
removeGradleReference (parentDir, subDir) {
const parentProjectFile = path.resolve(parentDir, 'project.properties');
const parentProperties = this._getPropertiesFile(parentProjectFile);
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
}
addSystemLibrary (parentDir, value) {
const parentProjectFile = path.resolve(parentDir, 'project.properties');
const parentProperties = this._getPropertiesFile(parentProjectFile);
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
addToPropertyList(parentProperties, 'cordova.system.library', value);
this._dirty = true;
}
removeSystemLibrary (parentDir, value) {
const parentProjectFile = path.resolve(parentDir, 'project.properties');
const parentProperties = this._getPropertiesFile(parentProjectFile);
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'cordova.system.library', value);
this._dirty = true;
}
@@ -144,8 +144,8 @@ class AndroidProject {
}
this._dirty = false;
for (const filename in this._propertiesEditors) {
const editor = this._propertiesEditors[filename];
for (var filename in this._propertiesEditors) {
var editor = this._propertiesEditors[filename];
if (editor.dirty) {
fs.writeFileSync(filename, editor.toString());
editor.dirty = false;
@@ -165,7 +165,7 @@ class AndroidProject {
* This checks if an Android project is clean or has old build artifacts
*/
isClean () {
const build_path = path.join(this.projectDir, 'build');
var build_path = path.join(this.projectDir, 'build');
// If the build directory doesn't exist, it's clean
return !(fs.existsSync(build_path));
}

View File

@@ -17,17 +17,17 @@
under the License.
*/
const path = require('path');
var path = require('path');
const AndroidProject = require('./AndroidProject');
const PluginManager = require('cordova-common').PluginManager;
var AndroidProject = require('./AndroidProject');
var PluginManager = require('cordova-common').PluginManager;
const CordovaLogger = require('cordova-common').CordovaLogger;
const selfEvents = require('cordova-common').events;
const ConfigParser = require('cordova-common').ConfigParser;
var CordovaLogger = require('cordova-common').CordovaLogger;
var selfEvents = require('cordova-common').events;
var ConfigParser = require('cordova-common').ConfigParser;
const prepare = require('./prepare').prepare;
const PLATFORM = 'android';
var PLATFORM = 'android';
const VERSION = require('../package').version;
function setupEvents (externalEventEmitter) {
@@ -73,8 +73,6 @@ class Api {
configXml: path.join(appRes, 'xml', 'config.xml'),
defaultConfigXml: path.join(this.root, 'cordova', 'defaults.xml'),
strings: path.join(appRes, 'values', 'strings.xml'),
themes: path.join(appRes, 'values', 'themes.xml'),
colors: path.join(appRes, 'values', 'colors.xml'),
manifest: path.join(appMain, 'AndroidManifest.xml'),
build: path.join(this.root, 'build'),
javaSrc: path.join(appMain, 'java')
@@ -90,7 +88,7 @@ class Api {
* platform's file structure and other properties of platform.
*/
getPlatformInfo () {
const result = {};
var result = {};
result.locations = this.locations;
result.root = this.root;
result.name = this.platform;
@@ -141,8 +139,8 @@ class Api {
* CordovaError instance.
*/
addPlugin (plugin, installOptions) {
const project = AndroidProject.getProjectFile(this.root);
const self = this;
var project = AndroidProject.getProjectFile(this.root);
var self = this;
installOptions = installOptions || {};
installOptions.variables = installOptions.variables || {};
@@ -177,7 +175,7 @@ class Api {
* CordovaError instance.
*/
removePlugin (plugin, uninstallOptions) {
const project = AndroidProject.getProjectFile(this.root);
var project = AndroidProject.getProjectFile(this.root);
if (uninstallOptions && uninstallOptions.usePlatformWww === true) {
uninstallOptions.usePlatformWww = false;
@@ -241,7 +239,7 @@ class Api {
* arhcitectures is specified.
*/
build (buildOptions) {
const self = this;
var self = this;
return require('./check_reqs').run().then(function () {
return require('./build').run.call(self, buildOptions);
@@ -271,7 +269,7 @@ class Api {
* successfully, or rejected with CordovaError.
*/
run (runOptions) {
const self = this;
var self = this;
return require('./check_reqs').run().then(function () {
return require('./run').run.call(self, runOptions);
});
@@ -285,7 +283,7 @@ class Api {
* CordovaError.
*/
clean (cleanOptions) {
const self = this;
var self = this;
// This will lint, checking for null won't
if (typeof cleanOptions === 'undefined') {
cleanOptions = {};
@@ -330,7 +328,7 @@ class Api {
*/
static createPlatform (destination, config, options, events) {
events = setupEvents(events);
let result;
var result;
try {
result = require('./create').create(destination, config, options, events).then(function (destination) {
return new Api(PLATFORM, destination, events);
@@ -360,7 +358,7 @@ class Api {
*/
static updatePlatform (destination, options, events) {
events = setupEvents(events);
let result;
var result;
try {
result = require('../../lib/create').update(destination, options, events).then(function (destination) {
return new Api(PLATFORM, destination, events);

View File

@@ -19,7 +19,7 @@
const execa = require('execa');
const suffix_number_regex = /(\d+)$/;
var suffix_number_regex = /(\d+)$/;
// Used for sorting Android targets, example strings to sort:
// android-19
// android-L
@@ -66,9 +66,9 @@ module.exports.version_string_to_api_level = {
/* eslint-enable quote-props */
function parse_targets (output) {
const target_out = output.split('\n');
const targets = [];
for (let i = target_out.length - 1; i >= 0; i--) {
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.
}

View File

@@ -17,15 +17,15 @@
under the License.
*/
const path = require('path');
const fs = require('fs');
const nopt = require('nopt');
var path = require('path');
var fs = require('fs');
var nopt = require('nopt');
const untildify = require('untildify');
const Adb = require('./Adb');
var Adb = require('./Adb');
const events = require('cordova-common').events;
const PackageType = require('./PackageType');
var events = require('cordova-common').events;
var PackageType = require('./PackageType');
module.exports.parseBuildOptions = parseOpts;
function parseOpts (options, resolvedTarget, projectRoot) {
@@ -46,7 +46,7 @@ function parseOpts (options, resolvedTarget, projectRoot) {
}, {}, options.argv, 0);
// Android Studio Build method is the default
const ret = {
var ret = {
buildType: options.release ? 'release' : 'debug',
prepEnv: options.argv.prepenv,
arch: resolvedTarget && resolvedTarget.arch,
@@ -61,7 +61,7 @@ function parseOpts (options, resolvedTarget, projectRoot) {
ret.extraArgs = ret.extraArgs.concat(options.argv.gradleArg);
}
const packageArgs = {};
var packageArgs = {};
if (options.argv.keystore) { packageArgs.keystore = path.relative(projectRoot, path.resolve(options.argv.keystore)); }
@@ -69,7 +69,7 @@ function parseOpts (options, resolvedTarget, projectRoot) {
if (options.argv[flagName]) { packageArgs[flagName] = options.argv[flagName]; }
});
const buildConfig = options.buildConfig;
var buildConfig = options.buildConfig;
// If some values are not specified as command line arguments - use build config to supplement them.
// Command line arguments have precedence over build config.
@@ -78,10 +78,10 @@ function parseOpts (options, resolvedTarget, projectRoot) {
throw new Error('Specified build config file does not exist: ' + buildConfig);
}
events.emit('log', 'Reading build config file: ' + path.resolve(buildConfig));
const buildjson = fs.readFileSync(buildConfig, 'utf8');
const config = JSON.parse(buildjson.replace(/^\ufeff/, '')); // Remove BOM
var buildjson = fs.readFileSync(buildConfig, 'utf8');
var config = JSON.parse(buildjson.replace(/^\ufeff/, '')); // Remove BOM
if (config.android && config.android[ret.buildType]) {
const androidInfo = config.android[ret.buildType];
var androidInfo = config.android[ret.buildType];
if (androidInfo.keystore && !packageArgs.keystore) {
androidInfo.keystore = untildify(androidInfo.keystore);
packageArgs.keystore = path.resolve(path.dirname(buildConfig), androidInfo.keystore);
@@ -144,8 +144,8 @@ function parseOpts (options, resolvedTarget, projectRoot) {
* Returns a promise.
*/
module.exports.runClean = function (options) {
const opts = parseOpts(options, null, this.root);
const builder = this._builder;
var opts = parseOpts(options, null, this.root);
var builder = this._builder;
return builder.prepEnv(opts).then(function () {
return builder.clean(opts);
@@ -165,8 +165,8 @@ module.exports.runClean = function (options) {
* information.
*/
module.exports.run = function (options, optResolvedTarget) {
const opts = parseOpts(options, optResolvedTarget, this.root);
const builder = this._builder;
var opts = parseOpts(options, optResolvedTarget, this.root);
var builder = this._builder;
return builder.prepEnv(opts).then(function () {
if (opts.prepEnv) {
@@ -174,7 +174,7 @@ module.exports.run = function (options, optResolvedTarget) {
return;
}
return builder.build(opts).then(function () {
let paths;
var paths;
if (opts.packageType === PackageType.BUNDLE) {
paths = builder.findOutputBundles(opts.buildType);
events.emit('log', 'Built the following bundle(s): \n\t' + paths.join('\n\t'));
@@ -184,7 +184,7 @@ module.exports.run = function (options, optResolvedTarget) {
}
return {
paths,
paths: paths,
buildType: opts.buildType
};
});
@@ -202,17 +202,17 @@ module.exports.detectArchitecture = function (target) {
};
module.exports.findBestApkForArchitecture = function (buildResults, arch) {
const paths = buildResults.apkPaths.filter(function (p) {
const apkName = path.basename(p);
var paths = buildResults.apkPaths.filter(function (p) {
var apkName = path.basename(p);
if (buildResults.buildType === 'debug') {
return /-debug/.exec(apkName);
}
return !/-debug/.exec(apkName);
});
const archPattern = new RegExp('-' + arch);
const hasArchPattern = /-x86|-arm/;
for (let i = 0; i < paths.length; ++i) {
const apkName = path.basename(paths[i]);
var archPattern = new RegExp('-' + arch);
var hasArchPattern = /-x86|-arm/;
for (var i = 0; i < paths.length; ++i) {
var apkName = path.basename(paths[i]);
if (hasArchPattern.exec(apkName)) {
if (archPattern.exec(apkName)) {
return paths[i];

View File

@@ -17,17 +17,16 @@
under the License.
*/
const fs = require('fs-extra');
const path = require('path');
var fs = require('fs-extra');
var path = require('path');
const execa = require('execa');
const glob = require('fast-glob');
const events = require('cordova-common').events;
const CordovaError = require('cordova-common').CordovaError;
const check_reqs = require('../check_reqs');
const PackageType = require('../PackageType');
var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;
var check_reqs = require('../check_reqs');
var PackageType = require('../PackageType');
const { compareByAll } = require('../utils');
const { createEditor } = require('properties-parser');
const CordovaGradleConfigParserFactory = require('../config/CordovaGradleConfigParserFactory');
const MARKER = 'YOUR CHANGES WILL BE ERASED!';
const SIGNING_PROPERTIES = '-signing.properties';
@@ -118,8 +117,8 @@ class ProjectBuilder {
* This returns a promise
*/
runGradleWrapper (gradle_cmd) {
const gradlePath = path.join(this.root, 'gradlew');
const wrapperGradle = path.join(this.root, 'wrapper.gradle');
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 {
@@ -129,35 +128,47 @@ class ProjectBuilder {
readProjectProperties () {
function findAllUniq (data, r) {
const s = {};
let m;
var s = {};
var m;
while ((m = r.exec(data))) {
s[m[1]] = 1;
}
return Object.keys(s);
}
const data = fs.readFileSync(path.join(this.root, 'project.properties'), 'utf8');
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),
bomPlatforms: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=platform\((?:'|")(.*)(?:'|")\)/mg)
systemLibs: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=(.*)(?:\s|$)/mg)
};
}
extractRealProjectNameFromManifest () {
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.
prepBuildFiles () {
// Update the version of build.gradle in each dependent library.
const pluginBuildGradle = path.join(__dirname, 'plugin-build.gradle');
const propertiesObj = this.readProjectProperties();
const subProjects = propertiesObj.libs;
var pluginBuildGradle = path.join(__dirname, '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
const checkAndCopy = function (subProject, root) {
const subProjectGradle = path.join(root, subProject, 'build.gradle');
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 {
@@ -167,46 +178,34 @@ class ProjectBuilder {
}
};
for (let i = 0; i < subProjects.length; ++i) {
for (var i = 0; i < subProjects.length; ++i) {
if (subProjects[i] !== 'CordovaLib') {
checkAndCopy(subProjects[i], this.root);
}
}
// get project name cdv-gradle-config.
const cdvGradleConfig = CordovaGradleConfigParserFactory.create(this.root);
const projectName = cdvGradleConfig.getProjectNameFromPackageName();
var name = this.extractRealProjectNameFromManifest();
// Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
const settingsGradlePaths = subProjects.map(function (p) {
const realDir = p.replace(/[/\\]/g, ':');
const libName = realDir.replace(projectName + '-', '');
let str = 'include ":' + libName + '"\n';
if (realDir.indexOf(projectName + '-') !== -1) {
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;
});
// Update subprojects within settings.gradle.
fs.writeFileSync(path.join(this.root, 'settings.gradle'),
'// GENERATED FILE - DO NOT EDIT\n' +
'apply from: "cdv-gradle-name.gradle"\n' +
'include ":"\n' +
settingsGradlePaths.join(''));
// Touch empty cdv-gradle-name.gradle file if missing.
if (!fs.pathExistsSync(path.join(this.root, 'cdv-gradle-name.gradle'))) {
fs.writeFileSync(path.join(this.root, 'cdv-gradle-name.gradle'), '');
}
'include ":"\n' + settingsGradlePaths.join(''));
// Update dependencies within build.gradle.
let buildGradle = fs.readFileSync(path.join(this.root, 'app', 'build.gradle'), 'utf8');
let depsList = '';
const root = this.root;
const insertExclude = function (p) {
const gradlePath = path.join(root, p, 'build.gradle');
const projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
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 {
@@ -215,39 +214,26 @@ class ProjectBuilder {
};
subProjects.forEach(function (p) {
events.emit('log', 'Subproject Path: ' + p);
const libName = p.replace(/[/\\]/g, ':').replace(projectName + '-', '');
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
const SYSTEM_LIBRARY_MAPPINGS = [
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.bomPlatforms.forEach(function (p) {
if (!/:.*:/.exec(p)) {
throw new CordovaError('Malformed BoM platform: ' + p);
}
// Add bom platform
depsList += ' implementation platform("' + p + '")\n';
});
propertiesObj.systemLibs.forEach(function (p) {
let mavenRef;
var mavenRef;
// It's already in gradle form if it has two ':'s
if (/:.*:/.exec(p)) {
mavenRef = p;
} else if (/:.*/.exec(p)) {
// Support BoM imports
mavenRef = p;
events.emit('warn', 'Library expects a BoM package: ' + p);
} else {
for (let i = 0; i < SYSTEM_LIBRARY_MAPPINGS.length; ++i) {
const pair = SYSTEM_LIBRARY_MAPPINGS[i];
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;
@@ -261,7 +247,7 @@ class ProjectBuilder {
});
buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + ' $2');
let includeList = '';
var includeList = '';
propertiesObj.gradleIncludes.forEach(function (includePath) {
includeList += 'apply from: "../' + includePath + '"\n';
@@ -272,7 +258,7 @@ class ProjectBuilder {
}
prepEnv (opts) {
const self = this;
var self = this;
return check_reqs.check_gradle()
.then(function (gradlePath) {
return self.runGradleWrapper(gradlePath);
@@ -315,8 +301,8 @@ class ProjectBuilder {
* Returns a promise.
*/
async build (opts) {
const wrapper = path.join(this.root, 'gradlew');
const args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
var wrapper = path.join(this.root, 'gradlew');
var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
try {
return await execa(wrapper, args, { stdio: 'inherit', cwd: path.resolve(this.root) });

View File

@@ -42,7 +42,7 @@ dependencies {
}
android {
compileSdkVersion cordovaConfig.COMPILE_SDK_VERSION
compileSdkVersion cordovaConfig.SDK_VERSION
buildToolsVersion cordovaConfig.BUILD_TOOLS_VERSION
compileOptions {

View File

@@ -18,12 +18,12 @@
*/
const execa = require('execa');
const path = require('path');
const fs = require('fs-extra');
var path = require('path');
var fs = require('fs-extra');
const { forgivingWhichSync, isWindows, isDarwin } = require('./utils');
const java = require('./env/java');
const { CordovaError, ConfigParser, events } = require('cordova-common');
const android_sdk = require('./android_sdk');
var android_sdk = require('./android_sdk');
const { SDK_VERSION } = require('./gradle-config-defaults');
// Re-exporting these for backwards compatibility and for unit testing.
@@ -44,31 +44,6 @@ module.exports.get_target = function (projectRoot) {
return `android-${Math.max(userTargetSdkVersion, SDK_VERSION)}`;
};
/**
* @param {string} projectRoot
* @returns {string} The android target in format "android-${target}"
*/
module.exports.get_compile = function (projectRoot) {
const userTargetSdkVersion = getUserTargetSdkVersion(projectRoot) || SDK_VERSION;
const userCompileSdkVersion = getUserCompileSdkVersion(projectRoot) || userTargetSdkVersion;
module.exports.isCompileSdkValid(userCompileSdkVersion, userTargetSdkVersion);
return userCompileSdkVersion;
};
module.exports.isCompileSdkValid = (compileSdk, targetSdk) => {
targetSdk = (targetSdk || SDK_VERSION);
compileSdk = (compileSdk || targetSdk);
const isValid = compileSdk >= targetSdk;
if (!isValid) {
events.emit('warn', `The "android-compileSdkVersion" (${compileSdk}) should be greater than or equal to the "android-targetSdkVersion" (${targetSdk}).`);
}
return isValid;
};
/**
* @param {string} projectRoot
* @returns {number} target sdk or 0 if undefined
@@ -86,36 +61,19 @@ function getUserTargetSdkVersion (projectRoot) {
return isNaN(targetSdkVersion) ? 0 : targetSdkVersion;
}
/**
* @param {string} projectRoot
* @returns {number} target sdk or 0 if undefined
*/
function getUserCompileSdkVersion (projectRoot) {
// If the repo config.xml file exists, find the desired compileSdkVersion.
// We need to use the cordova project's config.xml here, since the platform
// project's config.xml does not yet have the user's preferences when this
// function is called during `Api.createPlatform`.
const configFile = path.join(projectRoot, '../../config.xml');
if (!fs.existsSync(configFile)) return 0;
const configParser = new ConfigParser(configFile);
const compileSdkVersion = parseInt(configParser.getPreference('android-compileSdkVersion', 'android'), 10);
return isNaN(compileSdkVersion) ? 0 : compileSdkVersion;
}
module.exports.get_gradle_wrapper = function () {
let androidStudioPath;
let i = 0;
let foundStudio = false;
let program_dir;
var androidStudioPath;
var i = 0;
var foundStudio = false;
var program_dir;
// OK, This hack only works on Windows, not on Mac OS or Linux. We will be deleting this eventually!
if (module.exports.isWindows()) {
const result = execa.sync(path.join(__dirname, 'getASPath.bat'));
var result = execa.sync(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) {
const androidPath = path.join(process.env.ProgramFiles, 'Android') + '/';
var androidPath = path.join(process.env.ProgramFiles, 'Android') + '/';
if (fs.existsSync(androidPath)) {
program_dir = fs.readdirSync(androidPath);
while (i < program_dir.length && !foundStudio) {
@@ -134,7 +92,7 @@ module.exports.get_gradle_wrapper = function () {
}
if (androidStudioPath !== null && fs.existsSync(androidStudioPath)) {
const dirs = fs.readdirSync(androidStudioPath);
var dirs = fs.readdirSync(androidStudioPath);
if (dirs[0].split('-')[0] === 'gradle') {
return path.join(androidStudioPath, dirs[0], 'bin', 'gradle');
}
@@ -146,13 +104,13 @@ module.exports.get_gradle_wrapper = function () {
// Returns a promise. Called only by build and clean commands.
module.exports.check_gradle = function () {
const sdkDir = process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT;
var sdkDir = process.env.ANDROID_SDK_ROOT || process.env.ANDROID_HOME;
if (!sdkDir) {
return Promise.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.'));
'Might need to install Android SDK or set up \'ANDROID_SDK_ROOT\' env variable.'));
}
const gradlePath = module.exports.get_gradle_wrapper();
var gradlePath = module.exports.get_gradle_wrapper();
if (gradlePath.length !== 0) return Promise.resolve(gradlePath);
@@ -174,20 +132,19 @@ module.exports.check_java = async function () {
// Returns a promise.
module.exports.check_android = function () {
return Promise.resolve().then(function () {
let hasAndroidHome = false;
function maybeSetAndroidHome (value) {
if (!hasAndroidHome && fs.existsSync(value)) {
hasAndroidHome = true;
process.env.ANDROID_HOME = value;
process.env.ANDROID_SDK_ROOT = value;
}
}
const adbInPath = forgivingWhichSync('adb');
const avdmanagerInPath = forgivingWhichSync('avdmanager');
var adbInPath = forgivingWhichSync('adb');
var avdmanagerInPath = forgivingWhichSync('avdmanager');
var hasAndroidHome = false;
if (process.env.ANDROID_HOME) {
maybeSetAndroidHome(path.resolve(process.env.ANDROID_HOME));
if (process.env.ANDROID_SDK_ROOT) {
maybeSetAndroidHome(path.resolve(process.env.ANDROID_SDK_ROOT));
}
// First ensure ANDROID_HOME is set
@@ -240,15 +197,15 @@ module.exports.check_android = function () {
}
if (!hasAndroidHome) {
// If we dont have ANDROID_HOME, but we do have some tools on the PATH, try to infer from the tooling PATH.
let parentDir, grandParentDir;
// If we dont have ANDROID_SDK_ROOT, but we do have some tools on the PATH, try to infer from the tooling PATH.
var parentDir, grandParentDir;
if (adbInPath) {
parentDir = path.dirname(adbInPath);
grandParentDir = path.dirname(parentDir);
if (path.basename(parentDir) === 'platform-tools') {
maybeSetAndroidHome(grandParentDir);
} else {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' 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.');
}
@@ -259,26 +216,26 @@ module.exports.check_android = function () {
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 it manually.\n' +
throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' 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 it manually.\n' +
if (!process.env.ANDROID_SDK_ROOT) {
throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' 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)) {
throw new CordovaError('\'ANDROID_HOME\' environment variable is set to non-existent path: ' + process.env.ANDROID_HOME +
if (!fs.existsSync(process.env.ANDROID_SDK_ROOT)) {
throw new CordovaError('\'ANDROID_SDK_ROOT\' environment variable is set to non-existent path: ' + process.env.ANDROID_SDK_ROOT +
'\nTry update it manually to point to valid SDK directory.');
}
// Next let's make sure relevant parts of the SDK tooling is in our PATH
if (hasAndroidHome && !adbInPath) {
process.env.PATH += path.delimiter + path.join(process.env.ANDROID_HOME, 'platform-tools');
process.env.PATH += path.delimiter + path.join(process.env.ANDROID_SDK_ROOT, 'platform-tools');
}
if (hasAndroidHome && !avdmanagerInPath) {
process.env.PATH += path.delimiter + path.join(process.env.ANDROID_HOME, 'tools', 'bin');
process.env.PATH += path.delimiter + path.join(process.env.ANDROID_SDK_ROOT, 'tools', 'bin');
}
return hasAndroidHome;
});
@@ -290,7 +247,7 @@ module.exports.check_android_target = function (projectRoot) {
// android-L
// Google Inc.:Google APIs:20
// Google Inc.:Glass Development Kit Preview:20
const desired_api_level = module.exports.get_target(projectRoot);
var desired_api_level = module.exports.get_target(projectRoot);
return android_sdk.list_targets().then(function (targets) {
if (targets.indexOf(desired_api_level) >= 0) {
return targets;
@@ -302,11 +259,11 @@ module.exports.check_android_target = function (projectRoot) {
// Returns a promise.
module.exports.run = function () {
console.log('Checking Java JDK and Android SDK versions');
console.log('ANDROID_HOME=' + process.env.ANDROID_HOME + ' (recommended setting)');
console.log('ANDROID_SDK_ROOT=' + process.env.ANDROID_SDK_ROOT + ' (DEPRECATED)');
console.log('ANDROID_SDK_ROOT=' + process.env.ANDROID_SDK_ROOT + ' (recommended setting)');
console.log('ANDROID_HOME=' + process.env.ANDROID_HOME + ' (DEPRECATED)');
return Promise.all([this.check_java(), this.check_android()]).then(function (values) {
console.log('Using Android SDK: ' + (process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT));
console.log('Using Android SDK: ' + process.env.ANDROID_SDK_ROOT);
if (!values[1]) {
throw new CordovaError('Requirements check failed for Android SDK! Android SDK was not detected.');
@@ -322,12 +279,12 @@ module.exports.run = function () {
* (for example, check_android_target returns an array of android targets installed)
* @param {Boolean} installed Indicates whether the requirement is installed or not
*/
const Requirement = function (id, name, version, installed) {
var Requirement = function (id, name, version, installed) {
this.id = id;
this.name = name;
this.installed = installed || false;
this.metadata = {
version
version: version
};
};
@@ -339,14 +296,14 @@ const Requirement = function (id, name, version, installed) {
* @return Promise<Requirement[]> Array of requirements. Due to implementation, promise is always fulfilled.
*/
module.exports.check_all = function (projectRoot) {
const requirements = [
var requirements = [
new Requirement('java', 'Java JDK'),
new Requirement('androidSdk', 'Android SDK'),
new Requirement('androidTarget', 'Android target'),
new Requirement('gradle', 'Gradle')
];
const checkFns = [
var checkFns = [
this.check_java,
this.check_android,
this.check_android_target.bind(this, projectRoot),
@@ -356,7 +313,7 @@ module.exports.check_all = function (projectRoot) {
// Then execute requirement checks one-by-one
return checkFns.reduce(function (promise, checkFn, idx) {
// Update each requirement with results
const requirement = requirements[idx];
var requirement = requirements[idx];
return promise.then(checkFn).then(function (version) {
requirement.installed = true;
requirement.metadata.version = version;

View File

@@ -1,71 +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.
*/
const fs = require('fs-extra');
const path = require('path');
const events = require('cordova-common').events;
class CordovaGradleConfigParser {
/**
* Loads and Edits Gradle Properties File.
*
* Do not construct this directly. Use CordovaGradleConfigParserFactory instead.
*
* @param {String} platformDir is the path of the Android platform directory
*/
constructor (platformDir) {
this._cdvGradleConfigFilePath = path.join(platformDir, 'cdv-gradle-config.json');
this._cdvGradleConfig = this._readConfig(this._cdvGradleConfigFilePath);
}
/**
* Reads and parses the configuration JSON file
*
* @param {String} configPath
* @returns {Record<any, any>} The parsed JSON object representing the gradle config.
*/
_readConfig (configPath) {
return fs.readJSONSync(configPath, 'utf-8');
}
setPackageName (packageName) {
events.emit('verbose', '[Cordova Gradle Config] Setting "PACKAGE_NAMESPACE" to ' + packageName);
this._cdvGradleConfig.PACKAGE_NAMESPACE = packageName;
return this;
}
getPackageName () {
return this._cdvGradleConfig.PACKAGE_NAMESPACE;
}
getProjectNameFromPackageName () {
const packageName = this._cdvGradleConfig.PACKAGE_NAMESPACE;
return packageName.substring(packageName.lastIndexOf('.') + 1);
}
/**
* Saves any changes that has been made to the properties file.
*/
write () {
events.emit('verbose', '[Cordova Gradle Config] Saving File');
fs.writeJSONSync(this._cdvGradleConfigFilePath, this._cdvGradleConfig, 'utf-8');
}
}
module.exports = CordovaGradleConfigParser;

View File

@@ -1,35 +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.
*/
const CordovaGradleConfigParser = require('./CordovaGradleConfigParser');
/**
* Builds new gradle config parsers
*/
module.exports = class CordovaGradleConfigParserFactory {
/**
* Loads and Edits Gradle Properties File.
*
* @param {String} platformDir is the path of the Android platform directory
*/
static create (platformDir) {
return new CordovaGradleConfigParser(platformDir);
}
};

View File

@@ -17,16 +17,15 @@
under the License.
*/
const path = require('path');
const fs = require('fs-extra');
const utils = require('./utils');
const check_reqs = require('./check_reqs');
const ROOT = path.join(__dirname, '..');
var path = require('path');
var fs = require('fs-extra');
var utils = require('./utils');
var check_reqs = require('./check_reqs');
var ROOT = path.join(__dirname, '..');
const { createEditor } = require('properties-parser');
const CordovaGradleConfigParserFactory = require('./config/CordovaGradleConfigParserFactory');
const CordovaError = require('cordova-common').CordovaError;
const AndroidManifest = require('./AndroidManifest');
var CordovaError = require('cordova-common').CordovaError;
var AndroidManifest = require('./AndroidManifest');
// Export all helper functions, and make sure internally within this module, we
// reference these methods via the `exports` object - this helps with testing
@@ -38,15 +37,16 @@ exports.copyScripts = copyScripts;
exports.copyBuildRules = copyBuildRules;
exports.writeProjectProperties = writeProjectProperties;
exports.prepBuildFiles = prepBuildFiles;
exports.writeNameForAndroidStudio = writeNameForAndroidStudio;
function getFrameworkDir (projectPath, shared) {
return shared ? path.join(ROOT, 'framework') : path.join(projectPath, 'CordovaLib');
}
function copyJsAndLibrary (projectPath, shared, projectName, targetAPI) {
const nestedCordovaLibPath = getFrameworkDir(projectPath, false);
const srcCordovaJsPath = path.join(ROOT, 'templates', 'project', 'assets', 'www', 'cordova.js');
const app_path = path.join(projectPath, 'app', 'src', 'main');
var nestedCordovaLibPath = getFrameworkDir(projectPath, false);
var srcCordovaJsPath = path.join(ROOT, 'templates', 'project', 'assets', 'www', 'cordova.js');
var app_path = path.join(projectPath, 'app', 'src', 'main');
const platform_www = path.join(projectPath, 'platform_www');
fs.copySync(srcCordovaJsPath, path.join(app_path, 'assets', 'www', 'cordova.js'));
@@ -57,7 +57,7 @@ function copyJsAndLibrary (projectPath, shared, projectName, targetAPI) {
fs.copySync(srcCordovaJsPath, path.join(platform_www, 'cordova.js'));
if (shared) {
const relativeFrameworkPath = path.relative(projectPath, getFrameworkDir(projectPath, true));
var relativeFrameworkPath = path.relative(projectPath, getFrameworkDir(projectPath, true));
fs.symlinkSync(relativeFrameworkPath, nestedCordovaLibPath, 'dir');
} else {
fs.ensureDirSync(nestedCordovaLibPath);
@@ -74,9 +74,9 @@ function copyJsAndLibrary (projectPath, shared, projectName, targetAPI) {
}
function extractSubProjectPaths (data) {
const ret = {};
const r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg;
let m;
var ret = {};
var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg;
var m;
while ((m = r.exec(data))) {
ret[m[1]] = 1;
}
@@ -84,13 +84,13 @@ function extractSubProjectPaths (data) {
}
function writeProjectProperties (projectPath, target_api) {
const dstPath = path.join(projectPath, 'project.properties');
const templatePath = path.join(ROOT, 'templates', 'project', 'project.properties');
const srcPath = fs.existsSync(dstPath) ? dstPath : templatePath;
var dstPath = path.join(projectPath, 'project.properties');
var templatePath = path.join(ROOT, 'templates', 'project', 'project.properties');
var srcPath = fs.existsSync(dstPath) ? dstPath : templatePath;
let data = fs.readFileSync(srcPath, 'utf8');
var data = fs.readFileSync(srcPath, 'utf8');
data = data.replace(/^target=.*/m, 'target=' + target_api);
let subProjects = extractSubProjectPaths(data);
var subProjects = extractSubProjectPaths(data);
subProjects = subProjects.filter(function (p) {
return !(/^CordovaLib$/m.exec(p) ||
/[\\/]cordova-android[\\/]framework$/m.exec(p) ||
@@ -101,7 +101,7 @@ function writeProjectProperties (projectPath, target_api) {
if (!/\n$/.exec(data)) {
data += '\n';
}
for (let i = 0; i < subProjects.length; ++i) {
for (var i = 0; i < subProjects.length; ++i) {
data += 'android.library.reference.' + (i + 1) + '=' + subProjects[i] + '\n';
}
fs.writeFileSync(dstPath, data);
@@ -109,12 +109,12 @@ function writeProjectProperties (projectPath, target_api) {
// This makes no sense, what if you're building with a different build system?
function prepBuildFiles (projectPath) {
const buildModule = require('./builders/builders');
var buildModule = require('./builders/builders');
buildModule.getBuilder(projectPath).prepBuildFiles();
}
function copyBuildRules (projectPath, isLegacy) {
const srcDir = path.join(ROOT, 'templates', 'project');
var srcDir = path.join(ROOT, 'templates', 'project');
if (isLegacy) {
// The project's build.gradle is identical to the earlier build.gradle, so it should still work
@@ -130,8 +130,8 @@ function copyBuildRules (projectPath, isLegacy) {
}
function copyScripts (projectPath) {
const srcScriptsDir = path.join(ROOT, 'templates', 'cordova');
const destScriptsDir = path.join(projectPath, 'cordova');
var srcScriptsDir = path.join(ROOT, 'templates', 'cordova');
var destScriptsDir = path.join(projectPath, 'cordova');
// Delete old scripts directory if this is an update.
fs.removeSync(destScriptsDir);
// Copy in the new ones.
@@ -147,7 +147,7 @@ function validatePackageName (package_name) {
// Make the package conform to Java package types
// http://developer.android.com/guide/topics/manifest/manifest-element.html#package
// Enforce underscore limitation
const msg = 'Error validating package name. ';
var msg = 'Error validating package name. ';
if (!/^[a-zA-Z][a-zA-Z0-9_]+(\.[a-zA-Z][a-zA-Z0-9_]*)+$/.test(package_name)) {
return Promise.reject(new CordovaError(msg + 'Must look like: `com.company.Name`. Currently is: `' + package_name + '`'));
@@ -167,7 +167,7 @@ function validatePackageName (package_name) {
* otherwise.
*/
function validateProjectName (project_name) {
const msg = 'Error validating project name. ';
var msg = 'Error validating project name. ';
// Make sure there's something there
if (project_name === '') {
return Promise.reject(new CordovaError(msg + 'Project name cannot be empty'));
@@ -176,6 +176,19 @@ function validateProjectName (project_name) {
return Promise.resolve();
}
/**
* Write the name of the app in "platforms/android/.idea/.name" so that Android Studio can show that name in the
* project listing. This is helpful to quickly look in the Android Studio listing if there are so many projects in
* Android Studio.
*
* https://github.com/apache/cordova-android/issues/1172
*/
function writeNameForAndroidStudio (project_path, project_name) {
const ideaPath = path.join(project_path, '.idea');
fs.ensureDirSync(ideaPath);
fs.writeFileSync(path.join(ideaPath, '.name'), project_name);
}
/**
* Creates an android application with the given options.
*
@@ -198,37 +211,36 @@ exports.create = function (project_path, config, options, events) {
options = options || {};
// Set default values for path, package and name
project_path = path.relative(process.cwd(), project_path);
project_path = path.relative(process.cwd(), (project_path || 'CordovaExample'));
// Check if project already exists
if (fs.existsSync(project_path)) {
return Promise.reject(new CordovaError('Project already exists! Delete and recreate'));
}
const package_name = config.android_packageName() || config.packageName() || 'io.cordova.helloCordova';
const project_name = config.name() || 'Hello Cordova';
var package_name = config.android_packageName() || config.packageName() || 'my.cordova.project';
var project_name = config.name()
? config.name().replace(/[^\w.]/g, '_') : 'CordovaExample';
const safe_activity_name = config.android_activityName() || options.activityName || 'MainActivity';
const target_api = check_reqs.get_target(project_path);
const compile_api = check_reqs.get_compile(project_path);
var safe_activity_name = config.android_activityName() || options.activityName || 'MainActivity';
var target_api = check_reqs.get_target(project_path);
// Make the package conform to Java package types
return exports.validatePackageName(package_name)
.then(function () {
return exports.validateProjectName(project_name);
}).then(function () {
// Log the given values for the project
// Log the given values for the project
events.emit('log', 'Creating Cordova project for the Android platform:');
events.emit('log', '\tPath: ' + project_path);
events.emit('log', '\tPackage: ' + package_name);
events.emit('log', '\tName: ' + project_name);
events.emit('log', '\tActivity: ' + safe_activity_name);
events.emit('log', '\tAndroid Target SDK: ' + target_api);
events.emit('log', '\tAndroid Compile SDK: ' + compile_api);
events.emit('log', '\tAndroid target: ' + target_api);
events.emit('verbose', 'Copying android template project to ' + project_path);
const project_template_dir = options.customTemplate || path.join(ROOT, 'templates', 'project');
const app_path = path.join(project_path, 'app', 'src', 'main');
var project_template_dir = options.customTemplate || path.join(ROOT, 'templates', 'project');
var app_path = path.join(project_path, 'app', 'src', 'main');
// copy project template
fs.ensureDirSync(app_path);
@@ -243,33 +255,29 @@ exports.create = function (project_path, config, options, events) {
exports.copyJsAndLibrary(project_path, options.link, safe_activity_name, target_api);
// Set up ther Android Studio paths
const java_path = path.join(app_path, 'java');
const assets_path = path.join(app_path, 'assets');
const resource_path = path.join(app_path, 'res');
var java_path = path.join(app_path, 'java');
var assets_path = path.join(app_path, 'assets');
var resource_path = path.join(app_path, 'res');
fs.ensureDirSync(java_path);
fs.ensureDirSync(assets_path);
fs.ensureDirSync(resource_path);
// store package name in cdv-gradle-config
const cdvGradleConfig = CordovaGradleConfigParserFactory.create(project_path);
cdvGradleConfig.setPackageName(package_name)
.write();
// interpolate the activity name and package
const packagePath = package_name.replace(/\./g, path.sep);
const activity_dir = path.join(java_path, packagePath);
const activity_path = path.join(activity_dir, safe_activity_name + '.java');
var packagePath = package_name.replace(/\./g, path.sep);
var activity_dir = path.join(java_path, packagePath);
var activity_path = path.join(activity_dir, safe_activity_name + '.java');
fs.ensureDirSync(activity_dir);
fs.copySync(path.join(project_template_dir, 'Activity.java'), activity_path);
utils.replaceFileContents(activity_path, /__ACTIVITY__/, safe_activity_name);
utils.replaceFileContents(path.join(app_path, 'res', 'values', 'strings.xml'), /__NAME__/, utils.escape(project_name));
utils.replaceFileContents(path.join(app_path, 'res', 'values', 'strings.xml'), /__NAME__/, project_name);
utils.replaceFileContents(activity_path, /__ID__/, package_name);
const manifest = new AndroidManifest(path.join(project_template_dir, 'AndroidManifest.xml'));
manifest.getActivity().setName(safe_activity_name);
var manifest = new AndroidManifest(path.join(project_template_dir, 'AndroidManifest.xml'));
manifest.setPackageId(package_name)
.getActivity().setName(safe_activity_name);
const manifest_path = path.join(app_path, 'AndroidManifest.xml');
var manifest_path = path.join(app_path, 'AndroidManifest.xml');
manifest.write(manifest_path);
exports.copyScripts(project_path);
@@ -278,13 +286,14 @@ exports.create = function (project_path, config, options, events) {
// Link it to local android install.
exports.writeProjectProperties(project_path, target_api);
exports.prepBuildFiles(project_path);
exports.writeNameForAndroidStudio(project_path, project_name);
events.emit('log', generateDoneMessage('create', options.link));
}).then(() => project_path);
};
function generateDoneMessage (type, link) {
const pkg = require('../package');
let msg = 'Android project ' + (type === 'update' ? 'updated ' : 'created ') + 'with ' + pkg.name + '@' + pkg.version;
var pkg = require('../package');
var msg = 'Android project ' + (type === 'update' ? 'updated ' : 'created ') + 'with ' + pkg.name + '@' + pkg.version;
if (link) {
msg += ' and has a linked CordovaLib';
}
@@ -293,7 +302,7 @@ function generateDoneMessage (type, link) {
// Returns a promise.
exports.update = function (projectPath, options, events) {
const errorString =
var errorString =
'An in-place platform update is not supported. \n' +
'The `platforms` folder is always treated as a build artifact in the CLI workflow.\n' +
'To update your platform, you have to remove, then add your android platform again.\n' +

View File

@@ -19,13 +19,13 @@
const execa = require('execa');
const fs = require('fs-extra');
const android_versions = require('android-versions');
const path = require('path');
const Adb = require('./Adb');
const events = require('cordova-common').events;
const CordovaError = require('cordova-common').CordovaError;
const android_sdk = require('./android_sdk');
const which = require('which');
var android_versions = require('android-versions');
var path = require('path');
var Adb = require('./Adb');
var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;
var android_sdk = require('./android_sdk');
var which = require('which');
// constants
const ONE_SECOND = 1000; // in milliseconds
@@ -41,11 +41,11 @@ function forgivingWhichSync (cmd) {
module.exports.list_images_using_avdmanager = function () {
return execa('avdmanager', ['list', 'avd']).then(({ stdout: output }) => {
const response = output.split('\n');
const emulator_list = [];
for (let i = 1; i < response.length; i++) {
var response = output.split('\n');
var emulator_list = [];
for (var i = 1; i < response.length; i++) {
// To return more detailed information use img_obj
const img_obj = {};
var img_obj = {};
if (response[i].match(/Name:\s/)) {
img_obj.name = response[i].split('Name: ')[1].replace('\r', '');
if (response[i + 1].match(/Device:\s/)) {
@@ -74,9 +74,9 @@ module.exports.list_images_using_avdmanager = function () {
img_obj.target = img_obj.target.substr(0, img_obj.target.indexOf('(') - 1).trim();
}
}
const version_string = img_obj.target.replace(/Android\s+/, '');
var version_string = img_obj.target.replace(/Android\s+/, '');
const api_level = android_sdk.version_string_to_api_level[version_string];
var api_level = android_sdk.version_string_to_api_level[version_string];
if (api_level) {
img_obj.target += ' (API level ' + api_level + ')';
}
@@ -120,9 +120,9 @@ module.exports.list_images = function () {
// 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) {
const api_level = avd.target.match(/\d+/);
var api_level = avd.target.match(/\d+/);
if (api_level) {
const level = android_versions.get(api_level);
var level = android_versions.get(api_level);
if (level) {
avd.target = 'Android ' + level.semver + ' (API level ' + api_level + ')';
}
@@ -145,12 +145,12 @@ module.exports.best_image = function (project_target) {
// Just return undefined if there is no images
if (images.length === 0) return;
let closest = 9999;
let best = images[0];
for (const i in images) {
const target = images[i].target;
var closest = 9999;
var best = images[0];
for (var i in images) {
var target = images[i].target;
if (target && target.indexOf('API level') > -1) {
const num = parseInt(target.split('(API level ')[1].replace(')', ''));
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) {
@@ -173,10 +173,10 @@ exports.list_started = async () => {
* Returns a promise.
*/
module.exports.get_available_port = function () {
const self = this;
var self = this;
return self.list_started().then(function (emulators) {
for (let p = 5584; p >= 5554; p -= 2) {
for (var p = 5584; p >= 5554; p -= 2) {
if (emulators.indexOf('emulator-' + p) === -1) {
events.emit('verbose', 'Found available port: ' + p);
return p;
@@ -195,7 +195,7 @@ module.exports.get_available_port = function () {
* Returns a promise.
*/
module.exports.start = function (emulatorId, boot_timeout) {
const self = this;
var self = this;
return Promise.resolve().then(function () {
if (!emulatorId) {
@@ -205,8 +205,8 @@ module.exports.start = function (emulatorId, boot_timeout) {
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
const emulator_dir = path.dirname(which.sync('emulator'));
const args = ['-avd', emulatorId, '-port', port];
var emulator_dir = path.dirname(which.sync('emulator'));
var args = ['-avd', emulatorId, '-port', port];
// Don't wait for it to finish, since the emulator will probably keep running for a long time.
execa('emulator', args, { stdio: 'inherit', detached: true, cwd: emulator_dir })
.unref();
@@ -241,9 +241,9 @@ module.exports.start = function (emulatorId, boot_timeout) {
* Returns this emulator's ID in a promise.
*/
module.exports.wait_for_emulator = function (port) {
const self = this;
var self = this;
return Promise.resolve().then(function () {
const emulator_id = 'emulator-' + port;
var emulator_id = 'emulator-' + port;
return Adb.shell(emulator_id, 'getprop dev.bootcomplete').then(function (output) {
if (output.indexOf('1') >= 0) {
return emulator_id;
@@ -271,7 +271,7 @@ module.exports.wait_for_emulator = function (port) {
* time_remaining or passing a negative value will cause it to wait forever
*/
module.exports.wait_for_boot = function (emulator_id, time_remaining) {
const self = this;
var self = this;
return Adb.shell(emulator_id, 'getprop sys.boot_completed').then(function (output) {
if (output.match(/1/)) {
return true;

5
lib/env/java.js vendored
View File

@@ -98,9 +98,8 @@ const java = {
}
} else {
// See if we can derive it from javac's location.
const maybeJavaHome = path.dirname(path.dirname(javacPath));
if (fs.existsSync(path.join(maybeJavaHome, 'bin', 'java')) ||
fs.existsSync(path.join(maybeJavaHome, 'bin', 'java.exe'))) {
var maybeJavaHome = path.dirname(path.dirname(javacPath));
if (fs.existsSync(path.join(maybeJavaHome, 'lib', 'tools.jar'))) {
environment.JAVA_HOME = maybeJavaHome;
} else {
throw new CordovaError(default_java_error_msg);

View File

@@ -14,19 +14,19 @@
*
*/
const fs = require('fs-extra');
const path = require('path');
const isPathInside = require('is-path-inside');
const events = require('cordova-common').events;
const CordovaError = require('cordova-common').CordovaError;
var fs = require('fs-extra');
var path = require('path');
var isPathInside = require('is-path-inside');
var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;
const handlers = {
var handlers = {
'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));
const dest = getInstallDestination(obj);
var dest = getInstallDestination(obj);
if (options && options.force) {
copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
@@ -35,7 +35,7 @@ const handlers = {
}
},
uninstall: function (obj, plugin, project, options) {
const dest = getInstallDestination(obj);
var dest = getInstallDestination(obj);
// TODO: Add Koltin extension to uninstall, since they are handled like Java files
if (obj.src.endsWith('java')) {
@@ -48,35 +48,35 @@ const handlers = {
},
'lib-file': {
install: function (obj, plugin, project, options) {
const dest = path.join('app/libs', path.basename(obj.src));
var 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) {
const dest = path.join('app/libs', path.basename(obj.src));
var dest = path.join('app/libs', path.basename(obj.src));
removeFile(path.resolve(project.projectDir, dest));
}
},
'resource-file': {
install: function (obj, plugin, project, options) {
const dest = path.join('app', 'src', 'main', obj.target);
var dest = path.join('app', 'src', 'main', obj.target);
copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
},
uninstall: function (obj, plugin, project, options) {
const dest = path.join('app', 'src', 'main', obj.target);
var dest = path.join('app', 'src', 'main', obj.target);
removeFile(path.resolve(project.projectDir, dest));
}
},
framework: {
install: function (obj, plugin, project, options) {
const src = obj.src;
var src = obj.src;
if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
events.emit('verbose', 'Installing Android library: ' + src);
const parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
let subDir;
var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
var subDir;
if (obj.custom) {
const subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
var subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
copyNewFile(plugin.dir, src, project.projectDir, subRelativeDir, !!(options && options.link));
subDir = path.resolve(project.projectDir, subRelativeDir);
} else {
@@ -93,19 +93,19 @@ const handlers = {
}
},
uninstall: function (obj, plugin, project, options) {
const src = obj.src;
var src = obj.src;
if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
events.emit('verbose', 'Uninstalling Android library: ' + src);
const parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
let subDir;
var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
var subDir;
if (obj.custom) {
const subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
var subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
removeFile(path.resolve(project.projectDir, subRelativeDir));
subDir = path.resolve(project.projectDir, subRelativeDir);
// If it's the last framework in the plugin, remove the parent directory.
const parDir = path.dirname(subDir);
var parDir = path.dirname(subDir);
if (fs.existsSync(parDir) && fs.readdirSync(parDir).length === 0) {
fs.rmdirSync(parDir);
}
@@ -139,7 +139,7 @@ const handlers = {
}
},
uninstall: function (obj, plugin, project, options) {
const target = obj.target || obj.src;
var target = obj.target || obj.src;
if (!target) throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
@@ -155,29 +155,29 @@ const handlers = {
'js-module': {
install: function (obj, plugin, project, options) {
// Copy the plugin's files into the www directory.
const moduleSource = path.resolve(plugin.dir, obj.src);
const moduleName = plugin.id + '.' + (obj.name || path.basename(obj.src, path.extname(obj.src)));
var moduleSource = path.resolve(plugin.dir, 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.
let scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM
var scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM
if (moduleSource.match(/.*\.json$/)) {
scriptContent = 'module.exports = ' + scriptContent;
}
scriptContent = 'cordova.define("' + moduleName + '", function(require, exports, module) {\n' + scriptContent + '\n});\n';
const wwwDest = path.resolve(project.www, 'plugins', plugin.id, obj.src);
var wwwDest = path.resolve(project.www, 'plugins', plugin.id, obj.src);
fs.ensureDirSync(path.dirname(wwwDest));
fs.writeFileSync(wwwDest, scriptContent, 'utf-8');
if (options && options.usePlatformWww) {
// CB-11022 copy file to both directories if usePlatformWww is specified
const platformWwwDest = path.resolve(project.platformWww, 'plugins', plugin.id, obj.src);
var platformWwwDest = path.resolve(project.platformWww, 'plugins', plugin.id, obj.src);
fs.ensureDirSync(path.dirname(platformWwwDest));
fs.writeFileSync(platformWwwDest, scriptContent, 'utf-8');
}
},
uninstall: function (obj, plugin, project, options) {
const pluginRelativePath = path.join('plugins', plugin.id, obj.src);
var pluginRelativePath = path.join('plugins', plugin.id, obj.src);
removeFileAndParents(project.www, pluginRelativePath);
if (options && options.usePlatformWww) {
// CB-11022 remove file from both directories if usePlatformWww is specified
@@ -208,8 +208,8 @@ function copyFile (plugin_dir, src, project_dir, dest, link) {
if (!fs.existsSync(src)) throw new CordovaError('"' + src + '" not found!');
// check that src path is inside plugin directory
const real_path = fs.realpathSync(src);
const real_plugin_path = fs.realpathSync(plugin_dir);
var real_path = fs.realpathSync(src);
var real_plugin_path = fs.realpathSync(plugin_dir);
if (!isPathInside(real_path, real_plugin_path)) { throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"'); }
dest = path.resolve(project_dir, dest);
@@ -227,7 +227,7 @@ 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) {
const target_path = path.resolve(project_dir, dest);
var target_path = path.resolve(project_dir, dest);
if (fs.existsSync(target_path)) { throw new CordovaError('"' + target_path + '" already exists!'); }
copyFile(plugin_dir, src, project_dir, dest, !!link);
@@ -259,13 +259,13 @@ function deleteJava (project_dir, destFile) {
function removeFileAndParents (baseDir, destFile, stopper) {
stopper = stopper || '.';
const file = path.resolve(baseDir, destFile);
var file = path.resolve(baseDir, destFile);
if (!fs.existsSync(file)) return;
removeFile(file);
// check if directory is empty
let curDir = path.dirname(file);
var curDir = path.dirname(file);
while (curDir !== path.resolve(baseDir, stopper)) {
if (fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) {
@@ -283,16 +283,16 @@ function generateAttributeError (attribute, element, id) {
}
function getInstallDestination (obj) {
const APP_MAIN_PREFIX = 'app/src/main';
const PATH_SEPARATOR = '/';
var APP_MAIN_PREFIX = 'app/src/main';
var PATH_SEPARATOR = '/';
const PATH_SEP_MATCH = '\\' + PATH_SEPARATOR;
const PATH_SEP_OR_EOL_MATCH = '(\\' + PATH_SEPARATOR + '|$)';
var PATH_SEP_MATCH = '\\' + PATH_SEPARATOR;
var PATH_SEP_OR_EOL_MATCH = '(\\' + PATH_SEPARATOR + '|$)';
const appReg = new RegExp('^app' + PATH_SEP_OR_EOL_MATCH);
const libsReg = new RegExp('^libs' + PATH_SEP_OR_EOL_MATCH);
const srcReg = new RegExp('^src' + PATH_SEP_OR_EOL_MATCH);
const srcMainReg = new RegExp('^src' + PATH_SEP_MATCH + 'main' + PATH_SEP_OR_EOL_MATCH);
var appReg = new RegExp('^app' + PATH_SEP_OR_EOL_MATCH);
var libsReg = new RegExp('^libs' + PATH_SEP_OR_EOL_MATCH);
var srcReg = new RegExp('^src' + PATH_SEP_OR_EOL_MATCH);
var srcMainReg = new RegExp('^src' + PATH_SEP_MATCH + 'main' + PATH_SEP_OR_EOL_MATCH);
if (appReg.test(obj.targetDir)) {
// If any source file is using the new app directory structure,

View File

@@ -17,24 +17,24 @@
under the License.
*/
const fs = require('fs-extra');
const path = require('path');
var fs = require('fs-extra');
var path = require('path');
const nopt = require('nopt');
const glob = require('fast-glob');
const events = require('cordova-common').events;
const AndroidManifest = require('./AndroidManifest');
const xmlHelpers = require('cordova-common').xmlHelpers;
const CordovaError = require('cordova-common').CordovaError;
const ConfigParser = require('cordova-common').ConfigParser;
const FileUpdater = require('cordova-common').FileUpdater;
const PlatformJson = require('cordova-common').PlatformJson;
const PlatformMunger = require('cordova-common').ConfigChanges.PlatformMunger;
const PluginInfoProvider = require('cordova-common').PluginInfoProvider;
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;
var FileUpdater = require('cordova-common').FileUpdater;
var PlatformJson = require('cordova-common').PlatformJson;
var PlatformMunger = require('cordova-common').ConfigChanges.PlatformMunger;
var PluginInfoProvider = require('cordova-common').PluginInfoProvider;
const utils = require('./utils');
const gradleConfigDefaults = require('./gradle-config-defaults');
const checkReqs = require('./check_reqs');
const GradlePropertiesParser = require('./config/GradlePropertiesParser');
const CordovaGradleConfigParserFactory = require('./config/CordovaGradleConfigParserFactory');
function parseArguments (argv) {
return nopt({
@@ -44,15 +44,15 @@ function parseArguments (argv) {
}
module.exports.prepare = function (cordovaProject, options) {
const self = this;
var self = this;
let args = {};
if (options && options.options) {
args = parseArguments(options.options.argv);
}
const platformJson = PlatformJson.load(this.locations.root, this.platform);
const munger = new PlatformMunger(this.platform, this.locations.root, platformJson, new PluginInfoProvider());
var platformJson = PlatformJson.load(this.locations.root, this.platform);
var munger = new PlatformMunger(this.platform, this.locations.root, platformJson, new PluginInfoProvider());
this._config = updateConfigFilesFrom(cordovaProject.projectConfig, munger, this.locations);
@@ -63,15 +63,16 @@ module.exports.prepare = function (cordovaProject, options) {
updateUserProjectGradlePropertiesConfig(this, args);
// Update own www dir with project's www assets and plugins' assets and js-files
return Promise.resolve(updateWww(cordovaProject, this.locations))
.then(() => warnForDeprecatedSplashScreen(cordovaProject))
.then(() => updateProjectAccordingTo(self._config, self.locations))
.then(function () {
updateIcons(cordovaProject, path.relative(cordovaProject.root, self.locations.res));
updateFileResources(cordovaProject, path.relative(cordovaProject.root, self.locations.root));
}).then(function () {
events.emit('verbose', 'Prepared android project successfully');
});
return Promise.resolve(updateWww(cordovaProject, this.locations)).then(function () {
// update project according to config.xml changes.
return updateProjectAccordingTo(self._config, self.locations);
}).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 () {
events.emit('verbose', 'Prepared android project successfully');
});
};
/** @param {PlatformApi} project */
@@ -82,14 +83,6 @@ function updateUserProjectGradleConfig (project) {
...getUserGradleConfig(project._config)
};
// Check if compile sdk is valid.
// The returned result is iggnored and since we do not need and will not throw an error.
// Only using the valid check call for display the warning when target is greater then compiled.
checkReqs.isCompileSdkValid(
projectGradleConfig.COMPILE_SDK_VERSION,
projectGradleConfig.SDK_VERSION
);
// Write out changes
const projectGradleConfigPath = path.join(project.root, 'cdv-gradle-config.json');
fs.writeJSONSync(projectGradleConfigPath, projectGradleConfig, { spaces: 2 });
@@ -100,7 +93,6 @@ function getUserGradleConfig (configXml) {
{ xmlKey: 'android-minSdkVersion', gradleKey: 'MIN_SDK_VERSION', type: Number },
{ xmlKey: 'android-maxSdkVersion', gradleKey: 'MAX_SDK_VERSION', type: Number },
{ xmlKey: 'android-targetSdkVersion', gradleKey: 'SDK_VERSION', type: Number },
{ xmlKey: 'android-compileSdkVersion', gradleKey: 'COMPILE_SDK_VERSION', type: Number },
{ xmlKey: 'android-buildToolsVersion', gradleKey: 'BUILD_TOOLS_VERSION', type: String },
{ xmlKey: 'GradleVersion', gradleKey: 'GRADLE_VERSION', type: String },
{ xmlKey: 'AndroidGradlePluginVersion', gradleKey: 'AGP_VERSION', type: String },
@@ -159,18 +151,19 @@ module.exports.clean = function (options) {
// been called from the platform shell script rather than the CLI. Check for the
// noPrepare option passed in by the non-CLI clean script. If that's present, or if
// there's no config.xml found at the project root, then don't clean prepared files.
const projectRoot = path.resolve(this.root, '../..');
var projectRoot = path.resolve(this.root, '../..');
if ((options && options.noPrepare) || !fs.existsSync(this.locations.configXml) ||
!fs.existsSync(this.locations.configXml)) {
return Promise.resolve();
}
const projectConfig = new ConfigParser(this.locations.configXml);
var projectConfig = new ConfigParser(this.locations.configXml);
const self = this;
var self = this;
return Promise.resolve().then(function () {
cleanWww(projectRoot, self.locations);
cleanIcons(projectRoot, projectConfig, path.relative(projectRoot, self.locations.res));
cleanSplashes(projectRoot, projectConfig, path.relative(projectRoot, self.locations.res));
cleanFileResources(projectRoot, projectConfig, path.relative(projectRoot, self.locations.root));
});
};
@@ -202,7 +195,7 @@ function updateConfigFilesFrom (sourceConfig, configMunger, locations) {
events.emit('verbose', 'Merging project\'s config.xml into platform-specific android config.xml');
// Merge changes from app's config.xml into platform's one
const config = new ConfigParser(locations.configXml);
var config = new ConfigParser(locations.configXml);
xmlHelpers.mergeXml(sourceConfig.doc.getroot(),
config.doc.getroot(), 'android', /* clobber= */true);
@@ -227,19 +220,19 @@ function logFileOp (message) {
* paths for www files.
*/
function updateWww (cordovaProject, destinations) {
const sourceDirs = [
var sourceDirs = [
path.relative(cordovaProject.root, cordovaProject.locations.www),
path.relative(cordovaProject.root, destinations.platformWww)
];
// If project contains 'merges' for our platform, use them as another overrides
const merges_path = path.join(cordovaProject.root, 'merges', 'android');
var merges_path = path.join(cordovaProject.root, 'merges', 'android');
if (fs.existsSync(merges_path)) {
events.emit('verbose', 'Found "merges/android" folder. Copying its contents into the android project.');
sourceDirs.push(path.join('merges', 'android'));
}
const targetDir = path.relative(cordovaProject.root, destinations.www);
var targetDir = path.relative(cordovaProject.root, destinations.www);
events.emit(
'verbose', 'Merging and updating files from [' + sourceDirs.join(', ') + '] to ' + targetDir);
FileUpdater.mergeAndUpdateDir(
@@ -250,7 +243,7 @@ function updateWww (cordovaProject, destinations) {
* Cleans all files from the platform 'www' directory.
*/
function cleanWww (projectRoot, locations) {
const targetDir = path.relative(projectRoot, locations.www);
var targetDir = path.relative(projectRoot, locations.www);
events.emit('verbose', 'Cleaning ' + targetDir);
// No source paths are specified, so mergeAndUpdateDir() will clear the target directory.
@@ -266,35 +259,37 @@ function cleanWww (projectRoot, locations) {
* @param {Object} locations A map of locations for this platform
*/
function updateProjectAccordingTo (platformConfig, locations) {
updateProjectStrings(platformConfig, locations);
updateProjectSplashScreen(platformConfig, locations);
// Update app name by editing res/values/strings.xml
var strings = xmlHelpers.parseElementtreeSync(locations.strings);
const name = platformConfig.name();
var name = platformConfig.name();
strings.find('string[@name="app_name"]').text = name.replace(/'/g, '\\\'');
// Update app name for gradle project
fs.writeFileSync(path.join(locations.root, 'cdv-gradle-name.gradle'),
'// GENERATED FILE - DO NOT EDIT\n' +
'rootProject.name = "' + name.replace(/[/\\:<>"?*|]/g, '_') + '"\n');
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
const androidPkgName = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_');
var androidPkgName = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_');
// updating cdv-gradle-config with new androidPkgName.
const cdvGradleConfig = CordovaGradleConfigParserFactory.create(locations.root);
cdvGradleConfig.setPackageName(androidPkgName)
.write();
var manifest = new AndroidManifest(locations.manifest);
var manifestId = manifest.getPackageId();
const manifest = new AndroidManifest(locations.manifest);
manifest.getActivity()
.setOrientation(platformConfig.getPreference('orientation'))
.setLaunchMode(findAndroidLaunchModePreference(platformConfig));
manifest.setVersionName(platformConfig.version())
.setVersionCode(platformConfig.android_versionCode() || default_versionCode(platformConfig.version()))
.setPackageId(androidPkgName)
.write();
// Java file paths shouldn't be hard coded
const javaDirectory = path.join(locations.javaSrc, androidPkgName.replace(/\./g, '/'));
const javaDirectory = path.join(locations.javaSrc, manifestId.replace(/\./g, '/'));
const java_files = glob.sync('**/*.java', { cwd: javaDirectory, absolute: true }).filter(f => {
const contents = fs.readFileSync(f, 'utf-8');
return /extends\s+CordovaActivity/.test(contents);
@@ -306,23 +301,27 @@ function updateProjectAccordingTo (platformConfig, locations) {
events.emit('log', 'Multiple candidate Java files that extend CordovaActivity found. Guessing at the first one, ' + java_files[0]);
}
const destFile = path.normalize(java_files[0]);
const destFile = java_files[0];
// if package name has changed, path to MainActivity.java has to track it
const newDestFile = path.join(locations.root, 'app', 'src', 'main', 'java', androidPkgName.replace(/\./g, '/'), path.basename(destFile));
if (newDestFile.toLowerCase() !== destFile.toLowerCase()) {
// If package was name changed we need to create new java with main activity in path matching new package name
fs.ensureDirSync(path.dirname(newDestFile));
events.emit('verbose', `copy ${destFile} to ${newDestFile}`);
fs.copySync(destFile, newDestFile);
utils.replaceFileContents(newDestFile, /package [\w.]*;/, 'package ' + androidPkgName + ';');
events.emit('verbose', 'Wrote out Android package name "' + androidPkgName + '" to ' + newDestFile);
// var destFile = path.join(locations.root, 'app', 'src', 'main', 'java', androidPkgName.replace(/\./g, '/'), path.basename(java_files[0]));
// fs.ensureDirSync(path.dirname(destFile));
// events.emit('verbose', java_files[0]);
// events.emit('verbose', destFile);
// console.log(locations);
// fs.copySync(java_files[0], destFile);
utils.replaceFileContents(destFile, /package [\w.]*;/, 'package ' + androidPkgName + ';');
events.emit('verbose', 'Wrote out Android package name "' + androidPkgName + '" to ' + destFile);
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
events.emit('verbose', `remove ${destFile}`);
fs.removeSync(destFile);
fs.removeSync(java_files[0]);
// remove any empty directories
let currentDir = path.dirname(destFile);
const sourcesRoot = path.resolve(locations.root, 'src');
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) {
fs.rmdirSync(currentDir);
@@ -334,288 +333,12 @@ function updateProjectAccordingTo (platformConfig, locations) {
}
}
/**
* Updates project structure and AndroidManifest according to project's configuration.
*
* @param {ConfigParser} platformConfig A project's configuration that will
* be used to update project
* @param {Object} locations A map of locations for this platform
*/
function updateProjectStrings (platformConfig, locations) {
// Update app name by editing res/values/strings.xml
const strings = xmlHelpers.parseElementtreeSync(locations.strings);
const name = platformConfig.name();
strings.find('string[@name="app_name"]').text = name.replace(/'/g, '\\\'');
const 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);
}
function warnForDeprecatedSplashScreen (cordovaProject) {
const hasOldSplashTags = (
cordovaProject.projectConfig.doc.findall('./platform[@name="android"]/splash') || []
).length > 0;
if (hasOldSplashTags) {
events.emit('warn', 'The "<splash>" tags were detected and are no longer supported. Please migrate to the "preference" tag "AndroidWindowSplashScreenAnimatedIcon".');
}
}
/**
* @param {ConfigParser} platformConfig A project's configuration that will
* be used to update project
* @param {Object} locations A map of locations for this platform
*/
function updateProjectSplashScreen (platformConfig, locations) {
// res/values/themes.xml
const themes = xmlHelpers.parseElementtreeSync(locations.themes);
const splashScreenTheme = themes.find('style[@name="Theme.App.SplashScreen"]');
[
'windowSplashScreenAnimatedIcon',
'windowSplashScreenAnimationDuration',
'windowSplashScreenBackground',
'android:windowSplashScreenBrandingImage',
'windowSplashScreenIconBackgroundColor',
'postSplashScreenTheme'
].forEach(themeKey => {
const index = themeKey.indexOf(':') + 1;
const cdvConfigPrefKey = 'Android' + themeKey.charAt(index).toUpperCase() + themeKey.slice(index + 1);
const cdvConfigPrefValue = platformConfig.getPreference(cdvConfigPrefKey, this.platform);
let themeTargetNode = splashScreenTheme.find(`item[@name="${themeKey}"]`);
switch (themeKey) {
case 'windowSplashScreenBackground':
// use the user defined value for "colors.xml"
updateProjectSplashScreenBackgroundColor(cdvConfigPrefValue, locations);
// force the themes value to `@color/cdv_splashscreen_background`
themeTargetNode.text = '@color/cdv_splashscreen_background';
break;
case 'windowSplashScreenAnimatedIcon':
// handle here the cases of "png" vs "xml" (drawable)
// If "png":
// - Clear out default or previous set "drawable/ic_cdv_splashscreen.xml" if exisiting.
// - Copy png in correct mipmap dir with name "ic_cdv_splashscreen.png"
// If "xml":
// - Clear out "{mipmap}/ic_cdv_splashscreen.png" if exisiting.
// - Copy xml into drawable dir with name "ic_cdv_splashscreen.xml"
// updateProjectSplashScreenIcon()
// value should change depending on case:
// If "png": "@mipmap/ic_cdv_splashscreen"
// If "xml": "@drawable/ic_cdv_splashscreen"
updateProjectSplashScreenImage(locations, themeKey, cdvConfigPrefKey, cdvConfigPrefValue);
break;
case 'android:windowSplashScreenBrandingImage':
// display warning only when set.
if (cdvConfigPrefValue) {
events.emit('warn', `"${themeKey}" is currently not supported by the splash screen compatibility library. https://issuetracker.google.com/issues/194301890`);
}
updateProjectSplashScreenImage(locations, themeKey, cdvConfigPrefKey, cdvConfigPrefValue);
// force the themes value to `@color/cdv_splashscreen_icon_background`
if (!cdvConfigPrefValue && themeTargetNode) {
splashScreenTheme.remove(themeTargetNode);
delete themes.getroot().attrib['xmlns:tools'];
} else if (cdvConfigPrefValue) {
// if there is no current node, create a new node.
if (!themeTargetNode) {
themeTargetNode = themes.getroot().makeelement('item', { name: themeKey, 'tools:targetApi': '31' });
splashScreenTheme.append(themeTargetNode);
themes.getroot().attrib['xmlns:tools'] = 'http://schemas.android.com/tools';
}
// set the user defined color.
themeTargetNode.text = '@drawable/ic_cdv_splashscreen_branding';
}
break;
case 'windowSplashScreenIconBackgroundColor':
// use the user defined value for "colors.xml"
updateProjectSplashScreenIconBackgroundColor(cdvConfigPrefValue, locations);
// force the themes value to `@color/cdv_splashscreen_icon_background`
if (!cdvConfigPrefValue && themeTargetNode) {
// currentItem.remove();
splashScreenTheme.remove(themeTargetNode);
} else if (cdvConfigPrefValue) {
// if there is no current color, create a new node.
if (!themeTargetNode) {
themeTargetNode = themes.getroot().makeelement('item', { name: themeKey });
splashScreenTheme.append(themeTargetNode);
}
// set the user defined color.
themeTargetNode.text = '@color/cdv_splashscreen_icon_background';
}
break;
case 'windowSplashScreenAnimationDuration':
themeTargetNode.text = cdvConfigPrefValue || '200';
break;
case 'postSplashScreenTheme':
themeTargetNode.text = cdvConfigPrefValue || '@style/Theme.AppCompat.NoActionBar';
break;
default:
events.emit('warn', `The theme property "${themeKey}" does not exist`);
}
});
fs.writeFileSync(locations.themes, themes.write({ indent: 4 }), 'utf-8');
events.emit('verbose', 'Wrote out Android application themes to ' + locations.themes);
}
/**
* @param {String} splashBackgroundColor SplashScreen Background Color Hex Code
* be used to update project
* @param {Object} locations A map of locations for this platform
*/
function updateProjectSplashScreenBackgroundColor (splashBackgroundColor, locations) {
if (!splashBackgroundColor) { splashBackgroundColor = '#FFFFFF'; }
// res/values/colors.xml
const colors = xmlHelpers.parseElementtreeSync(locations.colors);
colors.find('color[@name="cdv_splashscreen_background"]').text = splashBackgroundColor.replace(/'/g, '\\\'');
fs.writeFileSync(locations.colors, colors.write({ indent: 4 }), 'utf-8');
events.emit('verbose', 'Wrote out Android application SplashScreen Color to ' + locations.colors);
}
/**
* @param {String} splashIconBackgroundColor SplashScreen Icon Background Color Hex Code
* be used to update project
* @param {Object} locations A map of locations for this platform
*/
function updateProjectSplashScreenIconBackgroundColor (splashIconBackgroundColor, locations) {
// res/values/colors.xml
const colors = xmlHelpers.parseElementtreeSync(locations.colors);
// node name
const name = 'cdv_splashscreen_icon_background';
// get the current defined color
let currentColor = colors.find(`color[@name="${name}"]`);
if (!splashIconBackgroundColor && currentColor) {
colors.getroot().remove(currentColor);
} else if (splashIconBackgroundColor) {
// if there is no current color, create a new node.
if (!currentColor) {
currentColor = colors.getroot().makeelement('color', { name });
colors.getroot().append(currentColor);
}
// set the user defined color.
currentColor.text = splashIconBackgroundColor.replace(/'/g, '\\\'');
}
// write out the changes.
fs.writeFileSync(locations.colors, colors.write({ indent: 4 }), 'utf-8');
events.emit('verbose', 'Wrote out Android application SplashScreen Icon Color to ' + locations.colors);
}
function cleanupAndSetProjectSplashScreenImage (srcFile, destFilePath, possiblePreviousDestFilePath, cleanupOnly = false) {
if (fs.existsSync(possiblePreviousDestFilePath)) {
fs.removeSync(possiblePreviousDestFilePath);
}
if (cleanupOnly && fs.existsSync(destFilePath)) {
// Also remove dest file path for cleanup even if previous was not use.
fs.removeSync(destFilePath);
}
if (!cleanupOnly && srcFile && fs.existsSync(srcFile)) {
fs.copySync(srcFile, destFilePath);
}
}
function updateProjectSplashScreenImage (locations, themeKey, cdvConfigPrefKey, cdvConfigPrefValue = '') {
const SPLASH_SCREEN_IMAGE_BY_THEME_KEY = {
windowSplashScreenAnimatedIcon: 'ic_cdv_splashscreen',
'android:windowSplashScreenBrandingImage': 'ic_cdv_splashscreen_branding'
};
const destFileName = SPLASH_SCREEN_IMAGE_BY_THEME_KEY[themeKey] || null;
if (!destFileName) throw new CordovaError(`${themeKey} is not valid for image detection.`);
// Default paths of where images are saved
const destPngDir = path.join(locations.res, 'drawable-nodpi');
const destXmlDir = path.join(locations.res, 'drawable');
// Dest File Name and Path
const destFileNameExt = destFileName + '.xml';
let destFilePath = path.join(destXmlDir, destFileNameExt);
let possiblePreviousDestFilePath = path.join(destPngDir, destFileName + '.png');
// Default Drawable Source File
let defaultSrcFilePath = null;
if (themeKey !== 'android:windowSplashScreenBrandingImage') {
try {
// coming from user project
defaultSrcFilePath = require.resolve('cordova-android/templates/project/res/drawable/' + destFileNameExt);
} catch (e) {
// coming from repo test & coho
defaultSrcFilePath = require.resolve('../templates/project/res/drawable/' + destFileNameExt);
}
}
if (!cdvConfigPrefValue || !fs.existsSync(cdvConfigPrefValue)) {
let emitType = 'verbose';
let emmitMessage = `The "${cdvConfigPrefKey}" is undefined. Cordova's default will be used.`;
if (cdvConfigPrefValue && !fs.existsSync(cdvConfigPrefValue)) {
emitType = 'warn';
emmitMessage = `The "${cdvConfigPrefKey}" value does not exist. Cordova's default will be used.`;
}
events.emit(emitType, emmitMessage);
const cleanupOnly = themeKey === 'android:windowSplashScreenBrandingImage';
cleanupAndSetProjectSplashScreenImage(defaultSrcFilePath, destFilePath, possiblePreviousDestFilePath, cleanupOnly);
return;
}
const iconExtension = path.extname(cdvConfigPrefValue).toLowerCase();
if (iconExtension === '.png') {
// Put the image at this location.
destFilePath = path.join(destPngDir, destFileName + '.png');
// Check for this file and remove.
possiblePreviousDestFilePath = path.join(destXmlDir, destFileName + '.xml');
// copy the png to correct mipmap folder with name of ic_cdv_splashscreen.png
// delete ic_cdv_splashscreen.xml from drawable folder
// update themes.xml windowSplashScreenAnimatedIcon value to @mipmap/ic_cdv_splashscreen
cleanupAndSetProjectSplashScreenImage(cdvConfigPrefValue, destFilePath, possiblePreviousDestFilePath);
} else if (iconExtension === '.xml') {
// copy the xml to drawable folder with name of ic_cdv_splashscreen.xml
// delete ic_cdv_splashscreen.png from mipmap folder
// update themes.xml windowSplashScreenAnimatedIcon value to @drawable/ic_cdv_splashscreen
cleanupAndSetProjectSplashScreenImage(cdvConfigPrefValue, destFilePath, possiblePreviousDestFilePath);
} else {
// use the default destFilePath & possiblePreviousDestFilePath, no update require.
events.emit('warn', `The "${cdvConfigPrefKey}" had an unsupported extension. Cordova's default will be used.`);
cleanupAndSetProjectSplashScreenImage(defaultSrcFilePath, destFilePath, possiblePreviousDestFilePath);
}
}
// 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) {
const nums = version.split('-')[0].split('.');
let versionCode = 0;
var nums = version.split('-')[0].split('.');
var versionCode = 0;
if (+nums[0]) {
versionCode += +nums[0] * 10000;
}
@@ -633,8 +356,7 @@ function default_versionCode (version) {
function getImageResourcePath (resourcesDir, type, density, name, sourceName) {
// Use same extension as source with special case for 9-Patch files
const ext = sourceName.endsWith('.9.png')
? '.9.png'
: path.extname(sourceName).toLowerCase();
? '.9.png' : path.extname(sourceName).toLowerCase();
const subDir = density ? `${type}-${density}` : type;
return path.join(resourcesDir, subDir, name + ext);
@@ -644,10 +366,72 @@ function getAdaptiveImageResourcePath (resourcesDir, type, density, name, source
if (/\.9\.png$/.test(sourceName)) {
name = name.replace(/\.png$/, '.9.png');
}
const resourcePath = path.join(resourcesDir, (density ? type + '-' + density + '-v26' : type), name);
var resourcePath = path.join(resourcesDir, (density ? type + '-' + density + '-v26' : type), name);
return resourcePath;
}
function makeSplashCleanupMap (projectRoot, resourcesDir) {
// Build an initial resource map that deletes all existing splash screens
const existingSplashPaths = glob.sync(
`${resourcesDir.replace(/\\/g, '/')}/drawable-*/screen.{png,9.png,webp,jpg,jpeg}`,
{ cwd: projectRoot }
);
return makeCleanResourceMap(existingSplashPaths);
}
function updateSplashes (cordovaProject, platformResourcesDir) {
var resources = cordovaProject.projectConfig.getSplashScreens('android');
// if there are no "splash" elements in config.xml
if (resources.length === 0) {
events.emit('verbose', 'This app does not have splash screens defined');
// We must not return here!
// If the user defines no splash screens, the cleanup map will cause any
// existing splash screen images (e.g. the defaults that we copy into a
// new app) to be removed from the app folder, which is what we want.
}
// Build an initial resource map that deletes all existing splash screens
const resourceMap = makeSplashCleanupMap(cordovaProject.root, platformResourcesDir);
var hadMdpi = false;
resources.forEach(function (resource) {
if (!resource.density) {
return;
}
if (resource.density === 'mdpi') {
hadMdpi = true;
}
var targetPath = getImageResourcePath(
platformResourcesDir, 'drawable', resource.density, 'screen', path.basename(resource.src));
resourceMap[targetPath] = resource.src;
});
// There's no "default" drawable, so assume default == mdpi.
if (!hadMdpi && resources.defaultResource) {
var targetPath = getImageResourcePath(
platformResourcesDir, 'drawable', 'mdpi', 'screen', path.basename(resources.defaultResource.src));
resourceMap[targetPath] = resources.defaultResource.src;
}
events.emit('verbose', 'Updating splash screens at ' + platformResourcesDir);
FileUpdater.updatePaths(
resourceMap, { rootDir: cordovaProject.root }, logFileOp);
}
function cleanSplashes (projectRoot, projectConfig, platformResourcesDir) {
var resources = projectConfig.getSplashScreens('android');
if (resources.length > 0) {
const resourceMap = makeSplashCleanupMap(projectRoot, platformResourcesDir);
events.emit('verbose', 'Cleaning splash screens at ' + platformResourcesDir);
// No source paths are specified in the map, so updatePaths() will delete the target files.
FileUpdater.updatePaths(
resourceMap, { rootDir: projectRoot, all: true }, logFileOp);
}
}
function updateIcons (cordovaProject, platformResourcesDir) {
const icons = cordovaProject.projectConfig.getIcons('android');
@@ -706,10 +490,8 @@ function updateIcons (cordovaProject, platformResourcesDir) {
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher.png'),
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_foreground.png'),
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_background.png'),
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_monochrome.png'),
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_foreground.xml'),
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_background.xml'),
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_monochrome.xml'),
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher.xml')
);
@@ -729,27 +511,21 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
const android_icons = preparedIcons.android_icons;
const default_icon = preparedIcons.default_icon;
// The source paths for icons are relative to
// The source paths for icons and splashes are relative to
// project's config.xml location, so we use it as base path.
let background;
let foreground;
let monochrome;
let targetPathBackground;
let targetPathForeground;
let targetPathMonochrome;
for (const density in android_icons) {
let backgroundVal = '@mipmap/ic_launcher_background';
let foregroundVal = '@mipmap/ic_launcher_foreground';
const monochromeVal = '@mipmap/ic_launcher_monochrome';
background = android_icons[density].background;
foreground = android_icons[density].foreground;
monochrome = android_icons[density].monochrome;
const isAdaptiveIcon = background && foreground;
const isMonochromeIcon = monochrome && isAdaptiveIcon;
if (!isMonochromeIcon || !isAdaptiveIcon) {
if (!background || !foreground) {
// This icon isn't an adaptive icon, so skip it
continue;
}
@@ -780,34 +556,12 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
resourceMap[targetPathForeground] = android_icons[density].foreground;
}
if (monochrome) {
if (path.extname(path.basename(monochrome)) === '.xml') {
// Vector Use Case
targetPathMonochrome = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher_monochrome.xml', path.basename(android_icons[density].monochrome));
resourceMap[targetPathMonochrome] = android_icons[density].monochrome;
} else if (path.extname(path.basename(monochrome)) === '.png') {
// Images Use Case
targetPathMonochrome = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher_monochrome.png', path.basename(android_icons[density].monochrome));
resourceMap[targetPathMonochrome] = android_icons[density].monochrome;
}
}
// create an XML for DPI and set color
let icLauncherTemplate = '';
if (monochrome) {
icLauncherTemplate = `<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="` + backgroundVal + `" />
<foreground android:drawable="` + foregroundVal + `" />
<monochrome android:drawable="` + monochromeVal + `" />
</adaptive-icon>`;
} else {
icLauncherTemplate = `<?xml version="1.0" encoding="utf-8"?>
const icLauncherTemplate = `<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="` + backgroundVal + `" />
<foreground android:drawable="` + foregroundVal + `" />
</adaptive-icon>`;
}
const launcherXmlPath = path.join(platformResourcesDir, 'mipmap-' + density + '-v26', 'ic_launcher.xml');
@@ -821,7 +575,6 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
if (default_icon && !android_icons.mdpi) {
let defaultTargetPathBackground;
let defaultTargetPathForeground;
let defaultTargetPathMonochrome;
if (background.startsWith('@color')) {
// Colors Use Case
@@ -848,18 +601,6 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
defaultTargetPathForeground = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher_foreground.png', path.basename(default_icon.foreground));
resourceMap[defaultTargetPathForeground] = default_icon.foreground;
}
if (monochrome) {
if (path.extname(path.basename(monochrome)) === '.xml') {
// Vector Use Case
defaultTargetPathMonochrome = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher_monochrome.xml', path.basename(default_icon.monochrome));
resourceMap[defaultTargetPathMonochrome] = default_icon.monochrome;
} else if (path.extname(path.basename(monochrome)) === '.png') {
// Images Use Case
defaultTargetPathMonochrome = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher_monochrome.png', path.basename(default_icon.monochrome));
resourceMap[defaultTargetPathMonochrome] = default_icon.monochrome;
}
}
}
return resourceMap;
@@ -869,16 +610,16 @@ function updateIconResourceForLegacy (preparedIcons, resourceMap, platformResour
const android_icons = preparedIcons.android_icons;
const default_icon = preparedIcons.default_icon;
// The source paths for icons are relative to
// The source paths for icons and splashes are relative to
// project's config.xml location, so we use it as base path.
for (const density in android_icons) {
const targetPath = getImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher', path.basename(android_icons[density].src));
for (var density in android_icons) {
var targetPath = getImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher', path.basename(android_icons[density].src));
resourceMap[targetPath] = android_icons[density].src;
}
// There's no "default" drawable, so assume default == mdpi.
if (default_icon && !android_icons.mdpi) {
const defaultTargetPath = getImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher', path.basename(default_icon.src));
var defaultTargetPath = getImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher', path.basename(default_icon.src));
resourceMap[defaultTargetPath] = default_icon.src;
}
@@ -901,14 +642,14 @@ function prepareIcons (icons) {
// find the best matching icon for a given density or size
// @output android_icons
const parseIcon = function (icon, icon_size) {
var parseIcon = function (icon, icon_size) {
// do I have a platform icon for that density already
const density = icon.density || SIZE_TO_DENSITY_MAP[icon_size];
var density = icon.density || SIZE_TO_DENSITY_MAP[icon_size];
if (!density) {
// invalid icon defition ( or unsupported size)
return;
}
const previous = android_icons[density];
var previous = android_icons[density];
if (previous && previous.platform) {
return;
}
@@ -916,9 +657,9 @@ function prepareIcons (icons) {
};
// iterate over all icon elements to find the default icon and call parseIcon
for (let i = 0; i < icons.length; i++) {
const icon = icons[i];
let size = icon.width;
for (var i = 0; i < icons.length; i++) {
var icon = icons[i];
var size = icon.width;
if (!size) {
size = icon.height;
@@ -930,11 +671,6 @@ function prepareIcons (icons) {
const favor = {};
// populating found icon.
if (icon.background && icon.foreground && icon.monochrome) {
found.background = icon.background;
found.foreground = icon.foreground;
found.monochrome = icon.monochrome;
}
if (icon.background && icon.foreground) {
found.background = icon.background;
found.foreground = icon.foreground;
@@ -943,11 +679,6 @@ function prepareIcons (icons) {
found.src = icon.src;
}
if (default_icon.background && default_icon.foreground && default_icon.monochrome) {
favor.background = default_icon.background;
favor.foreground = default_icon.foreground;
favor.monochrome = default_icon.monochrome;
}
if (default_icon.background && default_icon.foreground) {
favor.background = default_icon.background;
favor.foreground = default_icon.foreground;
@@ -966,13 +697,13 @@ function prepareIcons (icons) {
}
return {
android_icons,
default_icon
android_icons: android_icons,
default_icon: default_icon
};
}
function cleanIcons (projectRoot, projectConfig, platformResourcesDir) {
const icons = projectConfig.getIcons('android');
var icons = projectConfig.getIcons('android');
// Skip if there are no app defined icons in config.xml
if (icons.length === 0) {
@@ -985,10 +716,8 @@ function cleanIcons (projectRoot, projectConfig, platformResourcesDir) {
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher.png'),
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_foreground.png'),
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_background.png'),
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_monochrome.png'),
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_foreground.xml'),
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_background.xml'),
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_monochrome.xml'),
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher.xml')
);
@@ -1011,8 +740,18 @@ function mapImageResources (rootDir, subDir, type, resourceName) {
return pathMap;
}
/** Returns resource map that deletes all given paths */
function makeCleanResourceMap (resourcePaths) {
const pathMap = {};
resourcePaths.map(path.normalize)
.forEach(resourcePath => {
pathMap[resourcePath] = null;
});
return pathMap;
}
function updateFileResources (cordovaProject, platformDir) {
const files = cordovaProject.projectConfig.getFileResources('android');
var files = cordovaProject.projectConfig.getFileResources('android');
// if there are resource-file elements in config.xml
if (files.length === 0) {
@@ -1020,9 +759,9 @@ function updateFileResources (cordovaProject, platformDir) {
return;
}
const resourceMap = {};
var resourceMap = {};
files.forEach(function (res) {
const targetPath = path.join(platformDir, res.target);
var targetPath = path.join(platformDir, res.target);
resourceMap[targetPath] = res.src;
});
@@ -1032,13 +771,13 @@ function updateFileResources (cordovaProject, platformDir) {
}
function cleanFileResources (projectRoot, projectConfig, platformDir) {
const files = projectConfig.getFileResources('android', true);
var files = projectConfig.getFileResources('android', true);
if (files.length > 0) {
events.emit('verbose', 'Cleaning resource files at ' + platformDir);
const resourceMap = {};
var resourceMap = {};
files.forEach(function (res) {
const filePath = path.join(platformDir, res.target);
var filePath = path.join(platformDir, res.target);
resourceMap[filePath] = null;
});
@@ -1059,14 +798,14 @@ function cleanFileResources (projectRoot, projectConfig, platformDir) {
* 'singleTop'
*/
function findAndroidLaunchModePreference (platformConfig) {
const launchMode = platformConfig.getPreference('AndroidLaunchMode');
var launchMode = platformConfig.getPreference('AndroidLaunchMode');
if (!launchMode) {
// Return a default value
return 'singleTop';
}
const expectedValues = ['standard', 'singleTop', 'singleTask', 'singleInstance'];
const valid = expectedValues.indexOf(launchMode) >= 0;
var expectedValues = ['standard', 'singleTop', 'singleTask', 'singleInstance'];
var valid = expectedValues.indexOf(launchMode) >= 0;
if (!valid) {
// Note: warn, but leave the launch mode as developer wanted, in case the list of options changes in the future
events.emit('warn', 'Unrecognized value for AndroidLaunchMode preference: ' +

View File

@@ -19,7 +19,7 @@
'use strict';
const events = require('cordova-common').events;
var events = require('cordova-common').events;
/**
* Retry a promise-returning function a number of times, propagating its

View File

@@ -17,13 +17,12 @@
under the License.
*/
const emulator = require('./emulator');
var emulator = require('./emulator');
const target = require('./target');
const build = require('./build');
const PackageType = require('./PackageType');
const AndroidManifest = require('./AndroidManifest');
const { CordovaError, events } = require('cordova-common');
const CordovaGradleConfigParserFactory = require('./config/CordovaGradleConfigParserFactory');
/**
* Builds a target spec from a runOptions object
@@ -79,7 +78,6 @@ module.exports.run = async function (runOptions = {}) {
}
const manifest = new AndroidManifest(this.locations.manifest);
const cordovaGradleConfigParser = CordovaGradleConfigParserFactory.create(this.locations.root);
return target.install(resolvedTarget, { manifest, buildResults, cordovaGradleConfigParser });
return target.install(resolvedTarget, { manifest, buildResults });
};

View File

@@ -127,9 +127,9 @@ exports.resolve = async (spec, buildResults) => {
};
};
exports.install = async function ({ id: target, arch, type }, { manifest, buildResults, cordovaGradleConfigParser }) {
exports.install = async function ({ id: target, arch, type }, { manifest, buildResults }) {
const apk_path = build.findBestApkForArchitecture(buildResults, arch);
const pkgName = cordovaGradleConfigParser.getPackageName();
const pkgName = manifest.getPackageId();
const launchName = pkgName + '/.' + manifest.getActivity().getName();
events.emit('log', 'Using apk: ' + apk_path);

View File

@@ -66,21 +66,3 @@ exports.forgivingWhichSync = (cmd) => {
exports.isWindows = () => os.platform() === 'win32';
exports.isDarwin = () => os.platform() === 'darwin';
const UNESCAPED_REGEX = /[&<>"']/g;
const escapes = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;'
};
/**
* Converts the characters "&", "<", ">", '"' and "'" in the given string to
* their corresponding escaped value
* @param {string} str the string to be escaped
* @returns the escaped string
*/
exports.escape = (str) => UNESCAPED_REGEX.test(str) ? str.replace(UNESCAPED_REGEX, (key) => escapes[key]) : str;

8094
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,7 @@
{
"name": "cordova-android",
"version": "12.0.0-dev",
"version": "10.1.0-dev",
"description": "cordova-android release",
"types": "./types/index.d.ts",
"main": "lib/Api.js",
"repository": "github:apache/cordova-android",
"bugs": "https://github.com/apache/cordova-android/issues",
@@ -13,41 +12,39 @@
],
"scripts": {
"prepare": "cordova-js build > templates/project/assets/www/cordova.js",
"test": "npm run lint && npm run cover && npm run java-unit-tests",
"test": "npm run lint && npm run cover && npm run java-tests",
"lint": "eslint lib spec test \"templates/cordova/**/!(*.*)\"",
"lint:fix": "npm run lint -- --fix",
"unit-tests": "jasmine --config=spec/unit/jasmine.json",
"cover": "nyc jasmine --config=spec/coverage.json",
"e2e-tests": "jasmine --config=spec/e2e/jasmine.json",
"java-unit-tests": "node test/run_java_unit_tests.js",
"clean:java-unit-tests": "node test/clean.js"
"java-tests": "node test/java_test_runner.js",
"clean:java-tests": "node test/clean.js"
},
"author": "Apache Software Foundation",
"license": "Apache-2.0",
"dependencies": {
"android-versions": "^1.8.1",
"cordova-common": "^5.0.0",
"android-versions": "^1.7.0",
"cordova-common": "^4.0.2",
"execa": "^5.1.1",
"fast-glob": "^3.2.12",
"fs-extra": "^11.1.1",
"fast-glob": "^3.2.7",
"fs-extra": "^10.0.0",
"is-path-inside": "^3.0.3",
"nopt": "^7.1.0",
"nopt": "^5.0.0",
"properties-parser": "^0.3.1",
"semver": "^7.3.8",
"semver": "^7.3.5",
"untildify": "^4.0.0",
"which": "^3.0.0"
"which": "^2.0.2"
},
"devDependencies": {
"@cordova/eslint-config": "^5.0.0",
"@cordova/eslint-config": "^3.0.0",
"cordova-js": "^6.1.0",
"elementtree": "^0.1.7",
"jasmine": "^4.6.0",
"jasmine": "^3.8.0",
"jasmine-spec-reporter": "^7.0.0",
"nyc": "^15.1.0",
"rewire": "^6.0.0"
"rewire": "^5.0.0"
},
"engines": {
"node": ">=16.13.0"
"node": ">=12.0.0"
},
"nyc": {
"include": [

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-all.zip

View File

@@ -110,6 +110,18 @@ describe('AndroidManifest', () => {
});
});
describe('packageId', () => {
it('should get the package ID', () => {
expect(manifest.getPackageId()).toBe(PACKAGE_ID);
});
it('should set the package ID', () => {
const newPackageId = `${PACKAGE_ID}new`;
manifest.setPackageId(newPackageId);
expect(manifest.getPackageId()).toBe(newPackageId);
});
});
describe('activity', () => {
let activity;

View File

@@ -19,8 +19,6 @@
const path = require('path');
const rewire = require('rewire');
const MockCordovaGradleConfigParser = require('./mocks/config/MockCordovaGradleConfigParser');
const CordovaGradleConfigParserFactory = require('../../lib/config/CordovaGradleConfigParserFactory');
describe('AndroidProject', () => {
const PROJECT_DIR = 'platforms/android';
@@ -32,8 +30,6 @@ describe('AndroidProject', () => {
AndroidStudioSpy = jasmine.createSpyObj('AndroidStudio', ['isAndroidStudioProject']);
AndroidProject.__set__('AndroidStudio', AndroidStudioSpy);
spyOn(CordovaGradleConfigParserFactory, 'create').and.returnValue(new MockCordovaGradleConfigParser(PROJECT_DIR));
});
describe('constructor', () => {
@@ -91,20 +87,26 @@ describe('AndroidProject', () => {
});
describe('getPackageName', () => {
let AndroidManifestSpy;
let AndroidManifestFns;
let androidProject;
beforeEach(() => {
AndroidManifestFns = jasmine.createSpyObj('AndroidManifestFns', ['getPackageId']);
AndroidManifestSpy = jasmine.createSpy('AndroidManifest').and.returnValue(AndroidManifestFns);
AndroidProject.__set__('AndroidManifest', AndroidManifestSpy);
androidProject = new AndroidProject(PROJECT_DIR);
});
it('should get the package name Cordova Gradle Config file', () => {
spyOn(MockCordovaGradleConfigParser.prototype, 'getPackageName');
it('should get the package name AndroidManifest', () => {
androidProject.getPackageName();
expect(MockCordovaGradleConfigParser.prototype.getPackageName).toHaveBeenCalled();
expect(AndroidManifestSpy).toHaveBeenCalledWith(path.join(PROJECT_DIR, 'app/src/main/AndroidManifest.xml'));
});
it('should return the package name', () => {
const packageName = 'io.cordova.unittest';
AndroidManifestFns.getPackageId.and.returnValue(packageName);
expect(androidProject.getPackageName()).toBe(packageName);
});

View File

@@ -17,29 +17,29 @@
under the License.
*/
const os = require('os');
const path = require('path');
const common = require('cordova-common');
var os = require('os');
var path = require('path');
var common = require('cordova-common');
const EventEmitter = require('events');
const Api = require('../../lib/Api');
const AndroidProject = require('../../lib/AndroidProject');
var Api = require('../../lib/Api');
var AndroidProject = require('../../lib/AndroidProject');
const PluginInfo = common.PluginInfo;
var PluginInfo = common.PluginInfo;
const FIXTURES = path.join(__dirname, '../e2e/fixtures');
const FAKE_PROJECT_DIR = path.join(os.tmpdir(), 'plugin-test-project');
var FIXTURES = path.join(__dirname, '../e2e/fixtures');
var FAKE_PROJECT_DIR = path.join(os.tmpdir(), 'plugin-test-project');
describe('Api', () => {
describe('addPlugin method', function () {
let api;
var api;
beforeEach(function () {
const pluginManager = jasmine.createSpyObj('pluginManager', ['addPlugin']);
var pluginManager = jasmine.createSpyObj('pluginManager', ['addPlugin']);
pluginManager.addPlugin.and.resolveTo();
spyOn(common.PluginManager, 'get').and.returnValue(pluginManager);
const projectSpy = jasmine.createSpyObj('AndroidProject', ['getPackageName', 'write', 'isClean']);
var projectSpy = jasmine.createSpyObj('AndroidProject', ['getPackageName', 'write', 'isClean']);
spyOn(AndroidProject, 'getProjectFile').and.returnValue(projectSpy);
api = new Api('android', FAKE_PROJECT_DIR, new EventEmitter());

View File

@@ -21,6 +21,8 @@ const fs = require('fs-extra');
const path = require('path');
const rewire = require('rewire');
const CordovaError = require('cordova-common').CordovaError;
describe('ProjectBuilder', () => {
const rootDir = '/root';
@@ -141,6 +143,30 @@ describe('ProjectBuilder', () => {
expect(execaSpy).not.toHaveBeenCalledWith('/my/sweet/gradle', jasmine.any(Array), jasmine.any(Object));
});
});
describe('extractRealProjectNameFromManifest', () => {
it('should get the project name from the Android Manifest', () => {
const projectName = 'unittestproject';
const projectId = `io.cordova.${projectName}`;
const manifest = `<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="${projectId}"></manifest>`;
spyOn(fs, 'readFileSync').and.returnValue(manifest);
expect(builder.extractRealProjectNameFromManifest()).toBe(projectName);
});
it('should throw an error if there is no package in the Android manifest', () => {
const manifest = `<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"></manifest>`;
spyOn(fs, 'readFileSync').and.returnValue(manifest);
expect(() => builder.extractRealProjectNameFromManifest()).toThrow(jasmine.any(CordovaError));
});
});
describe('build', () => {
beforeEach(() => {
spyOn(builder, 'getArgs');

View File

@@ -17,12 +17,12 @@
under the License.
*/
const rewire = require('rewire');
const android_sdk = require('../../lib/android_sdk');
const fs = require('fs-extra');
const path = require('path');
const events = require('cordova-common').events;
const which = require('which');
var rewire = require('rewire');
var android_sdk = require('../../lib/android_sdk');
var fs = require('fs-extra');
var path = require('path');
var events = require('cordova-common').events;
var which = require('which');
const {
SDK_VERSION: DEFAULT_TARGET_API
@@ -34,7 +34,7 @@ describe('check_reqs', function () {
check_reqs = rewire('../../lib/check_reqs');
});
let original_env;
var original_env;
beforeAll(function () {
original_env = Object.assign({}, process.env);
});
@@ -58,7 +58,7 @@ describe('check_reqs', function () {
});
describe('check_android', function () {
describe('find and set ANDROID_HOME when neither ANDROID_HOME nor ANDROID_SDK_ROOT is set', function () {
describe('find and set ANDROID_HOME when ANDROID_HOME and ANDROID_SDK_ROOT is not set', function () {
beforeEach(function () {
delete process.env.ANDROID_HOME;
delete process.env.ANDROID_SDK_ROOT;
@@ -68,20 +68,20 @@ describe('check_reqs', function () {
spyOn(which, 'sync').and.returnValue(null);
spyOn(fs, 'existsSync').and.returnValue(true);
});
it('it should set ANDROID_HOME on Windows', () => {
it('it should set ANDROID_SDK_ROOT on Windows', () => {
spyOn(check_reqs, 'isWindows').and.returnValue(true);
process.env.LOCALAPPDATA = 'windows-local-app-data';
process.env.ProgramFiles = 'windows-program-files';
return check_reqs.check_android().then(function () {
expect(process.env.ANDROID_HOME).toContain('windows-local-app-data');
expect(process.env.ANDROID_SDK_ROOT).toContain('windows-local-app-data');
});
});
it('it should set ANDROID_HOME on Darwin', () => {
it('it should set ANDROID_SDK_ROOT on Darwin', () => {
spyOn(check_reqs, 'isWindows').and.returnValue(false);
spyOn(check_reqs, 'isDarwin').and.returnValue(true);
process.env.HOME = 'home is where the heart is';
return check_reqs.check_android().then(function () {
expect(process.env.ANDROID_HOME).toContain('home is where the heart is');
expect(process.env.ANDROID_SDK_ROOT).toContain('home is where the heart is');
});
});
});
@@ -91,17 +91,17 @@ describe('check_reqs', function () {
return path;
});
});
it('should set ANDROID_HOME based on `adb` command if command exists in a SDK-like directory structure', () => {
it('should set ANDROID_SDK_ROOT based on `adb` command if command exists in a SDK-like directory structure', () => {
spyOn(fs, 'existsSync').and.returnValue(true);
spyOn(which, 'sync').and.callFake(function (cmd) {
if (cmd === 'adb') {
return path.normalize('/android/sdk/platform-tools/adb');
return '/android/sdk/platform-tools/adb';
} else {
return null;
}
});
return check_reqs.check_android().then(function () {
expect(process.env.ANDROID_HOME).toEqual(path.normalize('/android/sdk'));
expect(process.env.ANDROID_SDK_ROOT).toEqual('/android/sdk');
});
});
it('should error out if `adb` command exists in a non-SDK-like directory structure', () => {
@@ -119,17 +119,17 @@ describe('check_reqs', function () {
expect(err.message).toContain('update your PATH to include valid path');
});
});
it('should set ANDROID_HOME based on `avdmanager` command if command exists in a SDK-like directory structure', () => {
it('should set ANDROID_SDK_ROOT based on `avdmanager` command if command exists in a SDK-like directory structure', () => {
spyOn(fs, 'existsSync').and.returnValue(true);
spyOn(which, 'sync').and.callFake(function (cmd) {
if (cmd === 'avdmanager') {
return path.normalize('/android/sdk/tools/bin/avdmanager');
return '/android/sdk/tools/bin/avdmanager';
} else {
return null;
}
});
return check_reqs.check_android().then(function () {
expect(process.env.ANDROID_HOME).toEqual(path.normalize('/android/sdk'));
expect(process.env.ANDROID_SDK_ROOT).toEqual('/android/sdk');
});
});
it('should error out if `avdmanager` command exists in a non-SDK-like directory structure', () => {
@@ -150,52 +150,52 @@ describe('check_reqs', function () {
});
});
describe('ANDROID_HOME environment variable detection', () => {
describe('ANDROID_SDK_ROOT environment variable detection', () => {
beforeEach(() => {
delete process.env.ANDROID_HOME;
delete process.env.ANDROID_SDK_ROOT;
delete process.env.ANDROID_HOME;
check_reqs.__set__('forgivingWhichSync', jasmine.createSpy().and.returnValue(''));
});
const expectedAndroidSdkPath = path.sep + 'android' + path.sep + 'sdk';
const expectedAndroidRootSdkPath = path.sep + 'android' + path.sep + 'sdk' + path.sep + 'root';
it('should error if neither ANDROID_HOME nor ANDROID_SDK_ROOT is defined', () => {
it('should error if neither ANDROID_SDK_ROOT or ANDROID_HOME is defined', () => {
spyOn(fs, 'existsSync').and.returnValue(true);
return check_reqs.check_android().catch((error) => {
expect(error.toString()).toContain('Failed to find \'ANDROID_HOME\' environment variable.');
expect(error.toString()).toContain('Failed to find \'ANDROID_SDK_ROOT\' environment variable.');
});
});
it('should use ANDROID_HOME if defined', () => {
it('should use ANDROID_SDK_ROOT if defined', () => {
spyOn(fs, 'existsSync').and.returnValue(true);
process.env.ANDROID_HOME = path.normalize('/android/sdk');
process.env.ANDROID_SDK_ROOT = '/android/sdk';
return check_reqs.check_android().then(() => {
expect(process.env.ANDROID_HOME).toContain(expectedAndroidSdkPath);
expect(process.env.ANDROID_SDK_ROOT).toContain(expectedAndroidSdkPath);
});
});
it('should use ANDROID_SDK_ROOT if defined and ANDROID_HOME is not defined', () => {
it('should use ANDROID_HOME if defined and ANDROID_SDK_ROOT is not defined', () => {
spyOn(fs, 'existsSync').and.returnValue(true);
process.env.ANDROID_SDK_ROOT = path.normalize('/android/sdk/root');
process.env.ANDROID_HOME = '/android/sdk';
return check_reqs.check_android().then(() => {
expect(process.env.ANDROID_SDK_ROOT).toContain(expectedAndroidSdkPath);
});
});
it('should use ANDROID_SDK_ROOT if defined and ANDROID_HOME is defined', () => {
spyOn(fs, 'existsSync').and.returnValue(true);
process.env.ANDROID_SDK_ROOT = '/android/sdk/root';
process.env.ANDROID_HOME = '/android/sdk';
return check_reqs.check_android().then(() => {
expect(process.env.ANDROID_SDK_ROOT).toContain(expectedAndroidRootSdkPath);
});
});
it('should use ANDROID_HOME if defined and ANDROID_SDK_ROOT is defined', () => {
spyOn(fs, 'existsSync').and.returnValue(true);
process.env.ANDROID_HOME = path.normalize('/android/sdk');
process.env.ANDROID_SDK_ROOT = path.normalize('/android/sdk/root');
return check_reqs.check_android().then(() => {
expect(process.env.ANDROID_HOME).toContain(expectedAndroidSdkPath);
});
});
it('should throw if ANDROID_HOME points to an invalid path', () => {
process.env.ANDROID_HOME = path.normalize('/android/sdk');
it('should throw if ANDROID_SDK_ROOT points to an invalid path', () => {
process.env.ANDROID_SDK_ROOT = '/android/sdk';
return check_reqs.check_android().catch((error) => {
expect(error.toString()).toContain('\'ANDROID_HOME\' environment variable is set to non-existent path:');
expect(error.toString()).toContain('\'ANDROID_SDK_ROOT\' environment variable is set to non-existent path:');
});
});
});
@@ -203,7 +203,7 @@ describe('check_reqs', function () {
describe('set PATH for various Android binaries if not available', function () {
beforeEach(function () {
spyOn(which, 'sync').and.returnValue(null);
process.env.ANDROID_HOME = 'let the children play';
process.env.ANDROID_SDK_ROOT = 'let the children play';
spyOn(fs, 'existsSync').and.returnValue(true);
});
it('should add tools/bin,tools,platform-tools to PATH if `avdmanager`,`android`,`adb` is not found', () => {
@@ -219,30 +219,30 @@ describe('check_reqs', function () {
describe('check_gradle', () => {
describe('environment variable checks', () => {
beforeEach(() => {
delete process.env.ANDROID_HOME;
delete process.env.ANDROID_SDK_ROOT;
delete process.env.ANDROID_HOME;
spyOn(check_reqs, 'get_gradle_wrapper').and.callFake(() => {
return path.normalize((process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT) + '/bin/gradle');
return (process.env.ANDROID_SDK_ROOT || process.env.ANDROID_HOME) + '/bin/gradle';
});
});
it('with ANDROID_HOME / without ANDROID_SDK_ROOT', async () => {
process.env.ANDROID_HOME = path.normalize('/android/sdk/home');
await expectAsync(check_reqs.check_gradle()).toBeResolvedTo(path.normalize('/android/sdk/home/bin/gradle'));
it('with ANDROID_SDK_ROOT / without ANDROID_HOME', async () => {
process.env.ANDROID_SDK_ROOT = '/android/sdk/root';
await expectAsync(check_reqs.check_gradle()).toBeResolvedTo('/android/sdk/root/bin/gradle');
});
it('without ANDROID_HOME / with ANDROID_SDK_ROOT', async () => {
process.env.ANDROID_SDK_ROOT = path.normalize('/android/sdk/root');
await expectAsync(check_reqs.check_gradle()).toBeResolvedTo(path.normalize('/android/sdk/root/bin/gradle'));
it('with ANDROID_SDK_ROOT / with ANDROID_HOME', async () => {
process.env.ANDROID_SDK_ROOT = '/android/sdk/root';
process.env.ANDROID_HOME = '/android/sdk/home';
await expectAsync(check_reqs.check_gradle()).toBeResolvedTo('/android/sdk/root/bin/gradle');
});
it('with ANDROID_HOME / with ANDROID_SDK_ROOT', async () => {
process.env.ANDROID_HOME = path.normalize('/android/sdk/home');
process.env.ANDROID_SDK_ROOT = path.normalize('/android/sdk/root');
await expectAsync(check_reqs.check_gradle()).toBeResolvedTo(path.normalize('/android/sdk/home/bin/gradle'));
it('without ANDROID_SDK_ROOT / with ANDROID_HOME', async () => {
process.env.ANDROID_HOME = '/android/sdk/home';
await expectAsync(check_reqs.check_gradle()).toBeResolvedTo('/android/sdk/home/bin/gradle');
});
it('without ANDROID_HOME / without ANDROID_SDK_ROOT', () => {
it('without ANDROID_SDK_ROOT / without ANDROID_HOME', () => {
return check_reqs.check_gradle().catch((error) => {
expect(error.toString()).toContain('Could not find gradle wrapper within Android SDK. Could not find Android SDK directory.');
});
@@ -250,7 +250,7 @@ describe('check_reqs', function () {
});
it('should error if sdk is installed but no gradle found', () => {
process.env.ANDROID_HOME = path.normalize('/android/sdk');
process.env.ANDROID_SDK_ROOT = '/android/sdk';
spyOn(check_reqs, 'get_gradle_wrapper').and.callFake(() => {
return '';
});
@@ -263,8 +263,8 @@ describe('check_reqs', function () {
describe('get_target', function () {
const projectRoot = 'fakeProjectRoot';
let ConfigParser;
let getPreferenceSpy;
var ConfigParser;
var getPreferenceSpy;
beforeEach(function () {
getPreferenceSpy = jasmine.createSpy();
ConfigParser = jasmine.createSpy().and.returnValue({
@@ -274,7 +274,7 @@ describe('check_reqs', function () {
});
it('should retrieve DEFAULT_TARGET_API', function () {
const target = check_reqs.get_target(projectRoot);
var target = check_reqs.get_target(projectRoot);
expect(target).toBeDefined();
expect(target).toContain('android-' + DEFAULT_TARGET_API);
});
@@ -283,7 +283,7 @@ describe('check_reqs', function () {
spyOn(fs, 'existsSync').and.returnValue(true);
getPreferenceSpy.and.returnValue(String(DEFAULT_TARGET_API + 1));
const target = check_reqs.get_target(projectRoot);
var target = check_reqs.get_target(projectRoot);
expect(getPreferenceSpy).toHaveBeenCalledWith('android-targetSdkVersion', 'android');
expect(target).toBe('android-' + (DEFAULT_TARGET_API + 1));
@@ -293,7 +293,7 @@ describe('check_reqs', function () {
spyOn(fs, 'existsSync').and.returnValue(true);
getPreferenceSpy.and.returnValue('android-99');
const target = check_reqs.get_target(projectRoot);
var target = check_reqs.get_target(projectRoot);
expect(getPreferenceSpy).toHaveBeenCalledWith('android-targetSdkVersion', 'android');
expect(target).toBe('android-' + DEFAULT_TARGET_API);
@@ -306,7 +306,7 @@ describe('check_reqs', function () {
getPreferenceSpy.and.returnValue(String(DEFAULT_TARGET_API - 1));
const target = check_reqs.get_target(projectRoot);
var target = check_reqs.get_target(projectRoot);
expect(getPreferenceSpy).toHaveBeenCalledWith('android-targetSdkVersion', 'android');
expect(target).toBe('android-' + DEFAULT_TARGET_API);
@@ -316,7 +316,7 @@ describe('check_reqs', function () {
describe('check_android_target', function () {
it('should should return full list of supported targets if there is a match to ideal api level', () => {
const fake_targets = ['you are my fire', 'my one desire'];
var fake_targets = ['you are my fire', 'my one desire'];
spyOn(android_sdk, 'list_targets').and.resolveTo(fake_targets);
spyOn(check_reqs, 'get_target').and.returnValue('you are my fire');
return check_reqs.check_android_target().then(function (targets) {
@@ -325,7 +325,7 @@ describe('check_reqs', function () {
});
});
it('should error out if there is no match between ideal api level and installed targets', () => {
const fake_targets = ['you are my fire', 'my one desire'];
var fake_targets = ['you are my fire', 'my one desire'];
spyOn(android_sdk, 'list_targets').and.resolveTo(fake_targets);
spyOn(check_reqs, 'get_target').and.returnValue('and i knowwwwwwwwwwww');
return check_reqs.check_android_target().then(() => {

View File

@@ -17,25 +17,17 @@
under the License.
*/
const rewire = require('rewire');
const utils = require('../../lib/utils');
const create = rewire('../../lib/create');
const check_reqs = require('../../lib/check_reqs');
const fs = require('fs-extra');
const path = require('path');
const MockCordovaGradleConfigParser = require('./mocks/config/MockCordovaGradleConfigParser');
const CordovaGradleConfigParserFactory = require('../../lib/config/CordovaGradleConfigParserFactory');
var rewire = require('rewire');
var utils = require('../../lib/utils');
var create = rewire('../../lib/create');
var check_reqs = require('../../lib/check_reqs');
var fs = require('fs-extra');
var path = require('path');
describe('create', function () {
const PROJECT_DIR = 'platforms/android';
beforeAll(() => {
spyOn(CordovaGradleConfigParserFactory, 'create').and.returnValue(new MockCordovaGradleConfigParser(PROJECT_DIR));
});
describe('validatePackageName helper method', function () {
describe('happy path (valid package names)', function () {
const valid = [
var valid = [
'org.apache.mobilespec',
'com.example',
'com.floors42.package',
@@ -90,7 +82,7 @@ describe('create', function () {
describe('validateProjectName helper method', function () {
describe('happy path (valid project names)', function () {
const valid = [
var valid = [
'mobilespec',
'package_name',
'PackageName',
@@ -119,14 +111,14 @@ describe('create', function () {
});
describe('main method', function () {
let config_mock;
let events_mock;
const Manifest_mock = function () {};
let revert_manifest_mock;
const project_path = path.join('some', 'path');
const app_path = path.join(project_path, 'app', 'src', 'main');
const default_templates = path.join(__dirname, '..', '..', 'templates', 'project');
const fake_android_target = 'android-1337';
var config_mock;
var events_mock;
var Manifest_mock = function () {};
var revert_manifest_mock;
var project_path = path.join('some', 'path');
var app_path = path.join(project_path, 'app', 'src', 'main');
var default_templates = path.join(__dirname, '..', '..', 'templates', 'project');
var fake_android_target = 'android-1337';
beforeEach(function () {
Manifest_mock.prototype = jasmine.createSpyObj('AndroidManifest instance mock', ['setPackageId', 'getActivity', 'setName', 'write']);
@@ -140,6 +132,7 @@ describe('create', function () {
spyOn(create, 'copyBuildRules');
spyOn(create, 'writeProjectProperties');
spyOn(create, 'prepBuildFiles');
spyOn(create, 'writeNameForAndroidStudio');
revert_manifest_mock = create.__set__('AndroidManifest', Manifest_mock);
spyOn(fs, 'existsSync').and.returnValue(false);
spyOn(fs, 'copySync');
@@ -155,10 +148,10 @@ describe('create', function () {
});
describe('parameter values and defaults', function () {
it('should have a default package name of io.cordova.helloCordova', () => {
it('should have a default package name of my.cordova.project', () => {
config_mock.packageName.and.returnValue(undefined);
return create.create(project_path, config_mock, {}, events_mock).then(() => {
expect(create.validatePackageName).toHaveBeenCalledWith('io.cordova.helloCordova');
expect(create.validatePackageName).toHaveBeenCalledWith('my.cordova.project');
});
});
@@ -169,10 +162,10 @@ describe('create', function () {
});
});
it('should have a default project name of Hello Cordova', () => {
it('should have a default project name of CordovaExample', () => {
config_mock.name.and.returnValue(undefined);
return create.create(project_path, config_mock, {}, events_mock).then(() => {
expect(create.validateProjectName).toHaveBeenCalledWith('Hello Cordova');
expect(create.validateProjectName).toHaveBeenCalledWith('CordovaExample');
});
});
@@ -183,10 +176,10 @@ describe('create', function () {
});
});
it('should keep non-word characters (including unicode and spaces) in the ConfigParser-provided project name', () => {
it('should replace any non-word characters (including unicode and spaces) in the ConfigParser-provided project name with underscores', () => {
config_mock.name.and.returnValue('応応応応 hello 用用用用');
return create.create(project_path, config_mock, {}, events_mock).then(() => {
expect(create.validateProjectName).toHaveBeenCalledWith('応応応応 hello 用用用用');
expect(create.validateProjectName).toHaveBeenCalledWith('_____hello_____');
});
});
@@ -268,7 +261,7 @@ describe('create', function () {
it('should copy, rename and interpolate the template Activity java class with the project-specific activity name and package name', () => {
config_mock.packageName.and.returnValue('org.apache.cordova');
config_mock.android_activityName.and.returnValue('CEEDEEVEE');
const activity_path = path.join(app_path, 'java', 'org', 'apache', 'cordova', 'CEEDEEVEE.java');
var activity_path = path.join(app_path, 'java', 'org', 'apache', 'cordova', 'CEEDEEVEE.java');
return create.create(project_path, config_mock, {}, events_mock).then(() => {
expect(fs.copySync).toHaveBeenCalledWith(path.join(default_templates, 'Activity.java'), activity_path);
expect(utils.replaceFileContents).toHaveBeenCalledWith(activity_path, /__ACTIVITY__/, 'CEEDEEVEE');
@@ -283,13 +276,6 @@ describe('create', function () {
});
});
it('should interpolate the escaped project name into strings.xml', () => {
config_mock.name.and.returnValue('<Incredible&App>');
return create.create(project_path, config_mock, {}, events_mock).then(() => {
expect(utils.replaceFileContents).toHaveBeenCalledWith(path.join(app_path, 'res', 'values', 'strings.xml'), /__NAME__/, '&lt;Incredible&amp;App&gt;');
});
});
it('should copy template scripts into generated project', () => {
return create.create(project_path, config_mock, {}, events_mock).then(() => {
expect(create.copyScripts).toHaveBeenCalledWith(project_path);
@@ -315,4 +301,24 @@ describe('create', function () {
});
});
});
describe('writeNameForAndroidStudio', () => {
const project_path = path.join('some', 'path');
const appName = 'Test Cordova';
beforeEach(function () {
spyOn(fs, 'ensureDirSync');
spyOn(fs, 'writeFileSync');
});
it('should call ensureDirSync with path', () => {
create.writeNameForAndroidStudio(project_path, appName);
expect(fs.ensureDirSync).toHaveBeenCalledWith(path.join(project_path, '.idea'));
});
it('should call writeFileSync with content', () => {
create.writeNameForAndroidStudio(project_path, appName);
expect(fs.writeFileSync).toHaveBeenCalledWith(path.join(project_path, '.idea', '.name'), appName);
});
});
});

View File

@@ -1,32 +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.
*/
const CordovaGradleConfigParser = require('../../../../lib/config/CordovaGradleConfigParser');
module.exports = class MockCordoCordovaGradleConfigParservaGradleConfigParser extends CordovaGradleConfigParser {
_readConfig (configPath) {
return {
PACKAGE_NAMESPACE: 'io.cordova.unittest'
};
}
write () {
// Pretend write :)
}
};

View File

@@ -16,24 +16,24 @@
*
*/
const rewire = require('rewire');
const common = rewire('../../../lib/pluginHandlers');
const path = require('path');
const fs = require('fs-extra');
const osenv = require('os');
var rewire = require('rewire');
var common = rewire('../../../lib/pluginHandlers');
var path = require('path');
var fs = require('fs-extra');
var osenv = require('os');
const test_dir = path.join(osenv.tmpdir(), 'test_plugman');
const project_dir = path.join(test_dir, 'project');
const src = path.join(project_dir, 'src');
const dest = path.join(project_dir, 'dest');
const java_dir = path.join(src, 'one', 'two', 'three');
const java_file = path.join(java_dir, 'test.java');
const symlink_file = path.join(java_dir, 'symlink');
const non_plugin_file = path.join(osenv.tmpdir(), 'non_plugin_file');
var test_dir = path.join(osenv.tmpdir(), 'test_plugman');
var project_dir = path.join(test_dir, 'project');
var src = path.join(project_dir, 'src');
var dest = path.join(project_dir, 'dest');
var java_dir = path.join(src, 'one', 'two', 'three');
var java_file = path.join(java_dir, 'test.java');
var symlink_file = path.join(java_dir, 'symlink');
var non_plugin_file = path.join(osenv.tmpdir(), 'non_plugin_file');
const copyFile = common.__get__('copyFile');
const deleteJava = common.__get__('deleteJava');
const copyNewFile = common.__get__('copyNewFile');
var copyFile = common.__get__('copyFile');
var deleteJava = common.__get__('deleteJava');
var copyNewFile = common.__get__('copyNewFile');
describe('common platform handler', function () {
afterEach(() => {
@@ -50,7 +50,7 @@ describe('common platform handler', function () {
it('Test#002 : should throw if src not in plugin directory', function () {
fs.ensureDirSync(project_dir);
fs.outputFileSync(non_plugin_file, 'contents');
const outside_file = '../non_plugin_file';
var outside_file = '../non_plugin_file';
expect(function () { copyFile(test_dir, outside_file, project_dir, dest); })
.toThrow(new Error('File "' + path.resolve(test_dir, outside_file) + '" is located outside the plugin directory "' + test_dir + '"'));
});
@@ -88,8 +88,8 @@ describe('common platform handler', function () {
it('Test#006 : should call mkdir -p on target path', function () {
fs.outputFileSync(java_file, 'contents');
const s = spyOn(fs, 'ensureDirSync').and.callThrough();
const resolvedDest = path.resolve(project_dir, dest);
var s = spyOn(fs, 'ensureDirSync').and.callThrough();
var resolvedDest = path.resolve(project_dir, dest);
copyFile(test_dir, java_file, project_dir, dest);
@@ -100,8 +100,8 @@ describe('common platform handler', function () {
it('Test#007 : should call cp source/dest paths', function () {
fs.outputFileSync(java_file, 'contents');
const s = spyOn(fs, 'copySync').and.callThrough();
const resolvedDest = path.resolve(project_dir, dest);
var s = spyOn(fs, 'copySync').and.callThrough();
var resolvedDest = path.resolve(project_dir, dest);
copyFile(test_dir, java_file, project_dir, dest);
@@ -133,7 +133,7 @@ describe('common platform handler', function () {
});
it('Test#009 : should call fs.unlinkSync on the provided paths', function () {
const s = spyOn(fs, 'removeSync').and.callThrough();
var s = spyOn(fs, 'removeSync').and.callThrough();
deleteJava(project_dir, java_file);
expect(s).toHaveBeenCalled();
expect(s).toHaveBeenCalledWith(path.resolve(project_dir, java_file));

View File

@@ -17,43 +17,34 @@
under the License.
*/
const rewire = require('rewire');
const common = rewire('../../../lib/pluginHandlers');
const android = common.__get__('handlers');
const path = require('path');
const fs = require('fs-extra');
const os = require('os');
const temp = path.join(os.tmpdir(), 'plugman');
const plugins_dir = path.join(temp, 'cordova/plugins');
const dummyplugin = path.join(__dirname, '../../fixtures/org.test.plugins.dummyplugin');
const faultyplugin = path.join(__dirname, '../../fixtures/org.test.plugins.faultyplugin');
const android_studio_project = path.join(__dirname, '../../fixtures/android_studio_project');
var rewire = require('rewire');
var common = rewire('../../../lib/pluginHandlers');
var android = common.__get__('handlers');
var path = require('path');
var fs = require('fs-extra');
var os = require('os');
var temp = path.join(os.tmpdir(), 'plugman');
var plugins_dir = path.join(temp, 'cordova/plugins');
var dummyplugin = path.join(__dirname, '../../fixtures/org.test.plugins.dummyplugin');
var faultyplugin = path.join(__dirname, '../../fixtures/org.test.plugins.faultyplugin');
var android_studio_project = path.join(__dirname, '../../fixtures/android_studio_project');
const PluginInfo = require('cordova-common').PluginInfo;
const AndroidProject = require('../../../lib/AndroidProject');
var PluginInfo = require('cordova-common').PluginInfo;
var AndroidProject = require('../../../lib/AndroidProject');
const MockCordovaGradleConfigParser = require('../mocks/config/MockCordovaGradleConfigParser');
const CordovaGradleConfigParserFactory = require('../../../lib/config/CordovaGradleConfigParserFactory');
var dummyPluginInfo = new PluginInfo(dummyplugin);
var valid_source = dummyPluginInfo.getSourceFiles('android');
var valid_resources = dummyPluginInfo.getResourceFiles('android');
var valid_libs = dummyPluginInfo.getLibFiles('android');
const dummyPluginInfo = new PluginInfo(dummyplugin);
const valid_source = dummyPluginInfo.getSourceFiles('android');
const valid_resources = dummyPluginInfo.getResourceFiles('android');
const valid_libs = dummyPluginInfo.getLibFiles('android');
const faultyPluginInfo = new PluginInfo(faultyplugin);
const invalid_source = faultyPluginInfo.getSourceFiles('android');
var faultyPluginInfo = new PluginInfo(faultyplugin);
var invalid_source = faultyPluginInfo.getSourceFiles('android');
describe('android project handler', function () {
const PROJECT_DIR = 'platforms/android';
beforeAll(() => {
spyOn(CordovaGradleConfigParserFactory, 'create').and.returnValue(new MockCordovaGradleConfigParser(PROJECT_DIR));
});
describe('installation', function () {
const copyFileOrig = common.__get__('copyFile');
const copyFileSpy = jasmine.createSpy('copyFile');
let dummyProject;
var copyFileOrig = common.__get__('copyFile');
var copyFileSpy = jasmine.createSpy('copyFile');
var dummyProject;
beforeEach(function () {
fs.ensureDirSync(temp);
@@ -186,10 +177,10 @@ describe('android project handler', function () {
});
describe('of <framework> elements', function () {
const someString = jasmine.any(String);
var someString = jasmine.any(String);
const copyNewFileOrig = common.__get__('copyNewFile');
const copyNewFileSpy = jasmine.createSpy('copyNewFile');
var copyNewFileOrig = common.__get__('copyNewFile');
var copyNewFileSpy = jasmine.createSpy('copyNewFile');
beforeEach(function () {
fs.copySync(android_studio_project, temp);
@@ -209,34 +200,34 @@ describe('android project handler', function () {
});
it('Test#008 : should install framework without "parent" attribute into project root', function () {
const framework = { src: 'plugin-lib' };
var framework = { src: 'plugin-lib' };
android.framework.install(framework, dummyPluginInfo, dummyProject);
expect(dummyProject.addSystemLibrary).toHaveBeenCalledWith(dummyProject.projectDir, someString);
});
it('Test#009 : should install framework with "parent" attribute into parent framework dir', function () {
const childFramework = { src: 'plugin-lib2', parent: 'plugin-lib' };
var childFramework = { src: 'plugin-lib2', parent: 'plugin-lib' };
android.framework.install(childFramework, dummyPluginInfo, dummyProject);
expect(dummyProject.addSystemLibrary).toHaveBeenCalledWith(path.resolve(dummyProject.projectDir, childFramework.parent), someString);
});
it('Test#010 : should not copy anything if "custom" attribute is not set', function () {
const framework = { src: 'plugin-lib' };
const cpSpy = spyOn(fs, 'copySync');
var framework = { src: 'plugin-lib' };
var cpSpy = spyOn(fs, 'copySync');
android.framework.install(framework, dummyPluginInfo, dummyProject);
expect(dummyProject.addSystemLibrary).toHaveBeenCalledWith(someString, framework.src);
expect(cpSpy).not.toHaveBeenCalled();
});
it('Test#011 : should copy framework sources if "custom" attribute is set', function () {
const framework = { src: 'plugin-lib', custom: true };
var framework = { src: 'plugin-lib', custom: true };
android.framework.install(framework, dummyPluginInfo, dummyProject);
expect(dummyProject.addSubProject).toHaveBeenCalledWith(dummyProject.projectDir, someString);
expect(copyNewFileSpy).toHaveBeenCalledWith(dummyPluginInfo.dir, framework.src, dummyProject.projectDir, someString, false);
});
it('Test#012 : should install gradleReference using project.addGradleReference', function () {
const framework = { src: 'plugin-lib', custom: true, type: 'gradleReference' };
var framework = { src: 'plugin-lib', custom: true, type: 'gradleReference' };
android.framework.install(framework, dummyPluginInfo, dummyProject);
expect(copyNewFileSpy).toHaveBeenCalledWith(dummyPluginInfo.dir, framework.src, dummyProject.projectDir, someString, false);
expect(dummyProject.addGradleReference).toHaveBeenCalledWith(dummyProject.projectDir, someString);
@@ -244,8 +235,8 @@ describe('android project handler', function () {
});
describe('of <js-module> elements', function () {
const jsModule = { src: 'www/dummyplugin.js' };
let wwwDest, platformWwwDest;
var jsModule = { src: 'www/dummyplugin.js' };
var wwwDest, platformWwwDest;
beforeEach(function () {
spyOn(fs, 'writeFileSync');
@@ -267,7 +258,7 @@ describe('android project handler', function () {
});
describe('of <asset> elements', function () {
let asset;
var asset;
beforeEach(function () {
asset = { src: 'www/dummyPlugin.js', target: 'foo/dummy.js' };
@@ -288,10 +279,10 @@ describe('android project handler', function () {
});
describe('uninstallation', function () {
const deleteJavaOrig = common.__get__('deleteJava');
var deleteJavaOrig = common.__get__('deleteJava');
const originalRemoveSync = fs.removeSync;
const deleteJavaSpy = jasmine.createSpy('deleteJava');
let dummyProject;
var deleteJavaSpy = jasmine.createSpy('deleteJava');
var dummyProject;
let removeSyncSpy;
beforeEach(function () {
@@ -394,7 +385,7 @@ describe('android project handler', function () {
});
describe('of <framework> elements', function () {
const someString = jasmine.any(String);
var someString = jasmine.any(String);
beforeEach(function () {
fs.ensureDirSync(path.join(dummyProject.projectDir, dummyPluginInfo.id));
@@ -409,26 +400,26 @@ describe('android project handler', function () {
});
it('Test#021 : should uninstall framework without "parent" attribute into project root', function () {
const framework = { src: 'plugin-lib' };
var framework = { src: 'plugin-lib' };
android.framework.uninstall(framework, dummyPluginInfo, dummyProject);
expect(dummyProject.removeSystemLibrary).toHaveBeenCalledWith(dummyProject.projectDir, someString);
});
it('Test#022 : should uninstall framework with "parent" attribute into parent framework dir', function () {
const childFramework = { src: 'plugin-lib2', parent: 'plugin-lib' };
var childFramework = { src: 'plugin-lib2', parent: 'plugin-lib' };
android.framework.uninstall(childFramework, dummyPluginInfo, dummyProject);
expect(dummyProject.removeSystemLibrary).toHaveBeenCalledWith(path.resolve(dummyProject.projectDir, childFramework.parent), someString);
});
it('Test#023 : should remove framework sources if "custom" attribute is set', function () {
const framework = { src: 'plugin-lib', custom: true };
var framework = { src: 'plugin-lib', custom: true };
android.framework.uninstall(framework, dummyPluginInfo, dummyProject);
expect(dummyProject.removeSubProject).toHaveBeenCalledWith(dummyProject.projectDir, someString);
expect(removeSyncSpy).toHaveBeenCalledWith(someString);
});
it('Test#24 : should install gradleReference using project.removeGradleReference', function () {
const framework = { src: 'plugin-lib', custom: true, type: 'gradleReference' };
var framework = { src: 'plugin-lib', custom: true, type: 'gradleReference' };
android.framework.uninstall(framework, dummyPluginInfo, dummyProject);
expect(removeSyncSpy).toHaveBeenCalledWith(someString);
expect(dummyProject.removeGradleReference).toHaveBeenCalledWith(dummyProject.projectDir, someString);
@@ -436,15 +427,15 @@ describe('android project handler', function () {
});
describe('of <js-module> elements', function () {
const jsModule = { src: 'www/dummyPlugin.js' };
let wwwDest;
let platformWwwDest;
var jsModule = { src: 'www/dummyPlugin.js' };
var wwwDest;
var platformWwwDest;
beforeEach(function () {
wwwDest = path.resolve(dummyProject.www, 'plugins', dummyPluginInfo.id, jsModule.src);
platformWwwDest = path.resolve(dummyProject.platformWww, 'plugins', dummyPluginInfo.id, jsModule.src);
const existsSyncOrig = fs.existsSync;
var existsSyncOrig = fs.existsSync;
spyOn(fs, 'existsSync').and.callFake(function (file) {
if ([wwwDest, platformWwwDest].indexOf(file) >= 0) return true;
return existsSyncOrig.call(fs, file);
@@ -465,14 +456,14 @@ describe('android project handler', function () {
});
describe('of <asset> elements', function () {
const asset = { src: 'www/dummyPlugin.js', target: 'foo/dummy.js' };
let wwwDest, platformWwwDest;
var asset = { src: 'www/dummyPlugin.js', target: 'foo/dummy.js' };
var wwwDest, platformWwwDest;
beforeEach(function () {
wwwDest = path.resolve(dummyProject.www, asset.target);
platformWwwDest = path.resolve(dummyProject.platformWww, asset.target);
const existsSyncOrig = fs.existsSync;
var existsSyncOrig = fs.existsSync;
spyOn(fs, 'existsSync').and.callFake(function (file) {
if ([wwwDest, platformWwwDest].indexOf(file) >= 0) return true;
return existsSyncOrig.call(fs, file);

View File

@@ -17,14 +17,10 @@
under the License.
*/
const rewire = require('rewire');
const path = require('path');
const CordovaError = require('cordova-common').CordovaError;
var rewire = require('rewire');
var path = require('path');
var CordovaError = require('cordova-common').CordovaError;
const GradlePropertiesParser = require('../../lib/config/GradlePropertiesParser');
const utils = require('../../lib/utils');
const et = require('elementtree');
const MockCordovaGradleConfigParser = require('./mocks/config/MockCordovaGradleConfigParser');
const CordovaGradleConfigParserFactory = require('../../lib/config/CordovaGradleConfigParserFactory');
const PATH_RESOURCE = path.join('platforms', 'android', 'app', 'src', 'main', 'res');
@@ -53,10 +49,8 @@ function createResourceMap (target) {
if (!target || target === 'ic_launcher.png') resources[path.join(PATH_RESOURCE, mipmap, 'ic_launcher.png')] = null;
if (!target || target === 'ic_launcher_foreground.png') resources[path.join(PATH_RESOURCE, mipmap, 'ic_launcher_foreground.png')] = null;
if (!target || target === 'ic_launcher_background.png') resources[path.join(PATH_RESOURCE, mipmap, 'ic_launcher_background.png')] = null;
if (!target || target === 'ic_launcher_background.png') resources[path.join(PATH_RESOURCE, mipmap, 'ic_launcher_monochrome.png')] = null;
if (!target || target === 'ic_launcher_foreground.xml') resources[path.join(PATH_RESOURCE, mipmap, 'ic_launcher_foreground.xml')] = null;
if (!target || target === 'ic_launcher_background.xml') resources[path.join(PATH_RESOURCE, mipmap, 'ic_launcher_background.xml')] = null;
if (!target || target === 'ic_launcher_background.xml') resources[path.join(PATH_RESOURCE, mipmap, 'ic_launcher_monochrome.xml')] = null;
if (
!mipmap.includes('-v26') &&
@@ -87,6 +81,18 @@ function mockGetIconItem (data) {
}, data);
}
/**
* Create a mock item from the getSplashScreen collection with the supplied updated data.
*
* @param {Object} data Changes to apply to the mock getSplashScreen item
*/
function mockGetSplashScreenItem (data) {
return Object.assign({}, {
src: undefined,
density: undefined
}, data);
}
describe('prepare', () => {
// Rewire
let prepare;
@@ -95,12 +101,6 @@ describe('prepare', () => {
let emitSpy;
let updatePathsSpy;
const PROJECT_DIR = 'platforms/android';
beforeAll(() => {
spyOn(CordovaGradleConfigParserFactory, 'create').and.returnValue(new MockCordovaGradleConfigParser(PROJECT_DIR));
});
beforeEach(() => {
prepare = rewire('../../lib/prepare');
@@ -146,14 +146,10 @@ describe('prepare', () => {
return createResourceMap('ic_launcher_foreground.png');
} else if (resourceName.includes('ic_launcher_background.png')) {
return createResourceMap('ic_launcher_background.png');
} else if (resourceName.includes('ic_launcher_monochrome.png')) {
return createResourceMap('ic_launcher_monochrome.png');
} else if (resourceName.includes('ic_launcher_foreground.xml')) {
return createResourceMap('ic_launcher_foreground.xml');
} else if (resourceName.includes('ic_launcher_background.xml')) {
return createResourceMap('ic_launcher_background.xml');
} else if (resourceName.includes('ic_launcher_monochrome.xml')) {
return createResourceMap('ic_launcher_monochrome.xml');
} else if (resourceName.includes('ic_launcher.xml')) {
return createResourceMap('ic_launcher.xml');
}
@@ -319,9 +315,7 @@ describe('prepare', () => {
return [mockGetIconItem({
density: 'mdpi',
background: 'res/icon/android/mdpi-background.png',
foreground: 'res/icon/android/mdpi-foreground.xml',
monochrome: 'res/icon/android/mdpi-monochrome.png'
foreground: 'res/icon/android/mdpi-foreground.xml'
})];
};
@@ -359,8 +353,7 @@ describe('prepare', () => {
return [mockGetIconItem({
density: 'mdpi',
background: 'res/icon/android/mdpi-background.png',
foreground: 'res/icon/android/mdpi-foreground.png',
monochrome: 'res/icon/android/mdpi-monochrome.png'
foreground: 'res/icon/android/mdpi-foreground.png'
})];
};
@@ -369,7 +362,6 @@ describe('prepare', () => {
const phaseOneModification = {};
phaseOneModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_foreground.png')] = 'res/icon/android/mdpi-foreground.png';
phaseOneModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_background.png')] = 'res/icon/android/mdpi-background.png';
phaseOneModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_monochrome.png')] = 'res/icon/android/mdpi-monochrome.png';
const phaseOneUpdatedIconsForAdaptive = Object.assign({}, resourceMap, phaseOneModification);
updateIconResourceForAdaptiveSpy = jasmine.createSpy('updateIconResourceForAdaptiveSpy');
@@ -381,7 +373,6 @@ describe('prepare', () => {
const phaseTwoModification = {};
phaseTwoModification[path.join(PATH_RESOURCE, 'mipmap-mdpi', 'ic_launcher.png')] = 'res/icon/android/mdpi-foreground.png';
phaseTwoModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_background.png')] = 'res/icon/android/mdpi-background.png';
phaseTwoModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_monochrome.png')] = 'res/icon/android/mdpi-monochrome.png';
const phaseTwoUpdatedIconsForLegacy = Object.assign({}, phaseOneUpdatedIconsForAdaptive, phaseTwoModification);
updateIconResourceForLegacySpy = jasmine.createSpy('updateIconResourceForLegacySpy');
@@ -419,8 +410,7 @@ describe('prepare', () => {
density: 'mdpi',
src: 'res/icon/android/mdpi-icon.png',
background: 'res/icon/android/mdpi-background.png',
foreground: 'res/icon/android/mdpi-foreground.png',
monochrome: 'res/icon/android/mdpi-monochrome.png'
foreground: 'res/icon/android/mdpi-foreground.png'
})];
};
@@ -429,7 +419,6 @@ describe('prepare', () => {
const phaseOneModification = {};
phaseOneModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_foreground.png')] = 'res/icon/android/mdpi-foreground.png';
phaseOneModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_background.png')] = 'res/icon/android/mdpi-background.png';
phaseOneModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_monochrome.png')] = 'res/icon/android/mdpi-monochrome.png';
const phaseOneUpdatedIconsForAdaptive = Object.assign({}, resourceMap, phaseOneModification);
updateIconResourceForAdaptiveSpy = jasmine.createSpy('updateIconResourceForAdaptiveSpy');
@@ -441,7 +430,6 @@ describe('prepare', () => {
const phaseTwoModification = {};
phaseTwoModification[path.join(PATH_RESOURCE, 'mipmap-mdpi', 'ic_launcher.png')] = 'res/icon/android/mdpi-foreground.png';
phaseTwoModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_background.png')] = 'res/icon/android/mdpi-background.png';
phaseTwoModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_monochrome.png')] = 'res/icon/android/mdpi-monochrome.png';
const phaseTwoUpdatedIconsForLegacy = Object.assign({}, phaseOneUpdatedIconsForAdaptive, phaseTwoModification);
updateIconResourceForLegacySpy = jasmine.createSpy('updateIconResourceForLegacySpy');
@@ -533,15 +521,13 @@ describe('prepare', () => {
const ldpi = mockGetIconItem({
density: 'ldpi',
background: 'res/icon/android/ldpi-background.png',
foreground: 'res/icon/android/ldpi-foreground.png',
monochrome: 'res/icon/android/ldpi-monochrome.png'
foreground: 'res/icon/android/ldpi-foreground.png'
});
const mdpi = mockGetIconItem({
density: 'mdpi',
background: 'res/icon/android/mdpi-background.png',
foreground: 'res/icon/android/mdpi-foreground.png',
monochrome: 'res/icon/android/mdpi-monochrome.png'
foreground: 'res/icon/android/mdpi-foreground.png'
});
const icons = [ldpi, mdpi];
@@ -638,8 +624,7 @@ describe('prepare', () => {
mdpi: mockGetIconItem({
density: 'mdpi',
background: 'res/icon/android/mdpi-background.png',
foreground: 'res/icon/android/mdpi-foreground.png',
monochrome: 'res/icon/android/mdpi-monochrome.png'
foreground: 'res/icon/android/mdpi-foreground.png'
})
},
default_icon: undefined
@@ -661,7 +646,6 @@ describe('prepare', () => {
const expectedModification = {};
expectedModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_background.png')] = 'res/icon/android/mdpi-background.png';
expectedModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_foreground.png')] = 'res/icon/android/mdpi-foreground.png';
expectedModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_monochrome.png')] = 'res/icon/android/mdpi-monochrome.png';
const expected = Object.assign({}, resourceMap, expectedModification);
const actual = updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
@@ -694,8 +678,7 @@ describe('prepare', () => {
const icons = [mockGetIconItem({
density: 'mdpi',
background: 'res/icon/android/mdpi-background.png',
foreground: 'res/icon/android/mdpi-foreground.png',
monochrome: 'res/icon/android/mdpi-monochrome.png'
foreground: 'res/icon/android/mdpi-foreground.png'
})];
const projectRoot = '/mock';
const projectConfig = {
@@ -705,7 +688,7 @@ describe('prepare', () => {
};
const platformResourcesDir = PATH_RESOURCE;
const expectedResourceMapBackground = createResourceMap('ic_launcher_background.png');
var expectedResourceMapBackground = createResourceMap('ic_launcher_background.png');
// mocking initial responses for mapImageResources
prepare.__set__('mapImageResources', function (rootDir, subDir, type, resourceName) {
@@ -736,7 +719,7 @@ describe('prepare', () => {
};
const platformResourcesDir = PATH_RESOURCE;
const expectedResourceMap = createResourceMap();
var expectedResourceMap = createResourceMap();
// mocking initial responses for mapImageResources
prepare.__set__('mapImageResources', function (rootDir, subDir, type, resourceName) {
@@ -793,9 +776,8 @@ describe('prepare', () => {
prepare.__set__('updateWww', jasmine.createSpy());
prepare.__set__('updateProjectAccordingTo', jasmine.createSpy('updateProjectAccordingTo')
.and.returnValue(Promise.resolve()));
prepare.__set__('warnForDeprecatedSplashScreen', jasmine.createSpy('warnForDeprecatedSplashScreen')
.and.returnValue(Promise.resolve()));
prepare.__set__('updateIcons', jasmine.createSpy('updateIcons').and.returnValue(Promise.resolve()));
prepare.__set__('updateSplashes', jasmine.createSpy('updateSplashes').and.returnValue(Promise.resolve()));
prepare.__set__('updateFileResources', jasmine.createSpy('updateFileResources').and.returnValue(Promise.resolve()));
prepare.__set__('updateConfigFilesFrom',
jasmine.createSpy('updateConfigFilesFrom')
@@ -828,152 +810,109 @@ describe('prepare', () => {
});
});
describe('relocate CordovaActivity class java file', () => {
// Rewire
let Api;
let api;
let prepare;
// Spies
let replaceFileContents;
let ensureDirSyncSpy;
let copySyncSpy;
let removeSyncSpy;
describe('updateSplashes method', function () {
// Mock Data
let cordovaProject;
let options;
let packageName;
let initialJavaActivityPath;
beforeEach(() => {
Api = rewire('../../lib/Api');
prepare = rewire('../../lib/prepare');
let platformResourcesDir;
beforeEach(function () {
cordovaProject = {
root: '/mock',
projectConfig: {
path: '/mock/config.xml',
cdvNamespacePrefix: 'cdv',
shortName: () => 'rn',
name: () => 'rename',
android_versionCode: jasmine.createSpy('android_versionCode'),
android_packageName: () => packageName,
packageName: () => packageName,
getPreference: jasmine.createSpy('getPreference'),
version: () => '1.0.0'
cdvNamespacePrefix: 'cdv'
},
locations: {
plugins: '/mock/plugins',
www: '/mock/www',
strings: '/mock/res/values/strings.xml'
www: '/mock/www'
}
};
platformResourcesDir = PATH_RESOURCE;
api = new Api('android', cordovaProject.root);
initialJavaActivityPath = path.join(api.locations.javaSrc, 'com/company/product/MainActivity.java');
// mocking initial responses for mapImageResources
prepare.__set__('makeSplashCleanupMap', (rootDir, resourcesDir) => ({
[path.join(resourcesDir, 'drawable-mdpi/screen.png')]: null,
[path.join(resourcesDir, 'drawable-mdpi/screen.webp')]: null
}));
});
options = {
options: {}
it('Test#001 : Should detect no defined splash screens.', function () {
const updateSplashes = prepare.__get__('updateSplashes');
// mock data.
cordovaProject.projectConfig.getSplashScreens = function (platform) {
return [];
};
Api.__set__('ConfigParser',
jasmine.createSpy('ConfigParser')
.and.returnValue(cordovaProject.projectConfig)
);
updateSplashes(cordovaProject, platformResourcesDir);
Api.__set__('prepare', prepare.prepare);
// The emit was called
expect(emitSpy).toHaveBeenCalled();
prepare.__set__('updateWww', jasmine.createSpy('updateWww'));
prepare.__set__('updateIcons', jasmine.createSpy('updateIcons').and.returnValue(Promise.resolve()));
prepare.__set__('updateProjectSplashScreen', jasmine.createSpy('updateProjectSplashScreen'));
prepare.__set__('warnForDeprecatedSplashScreen', jasmine.createSpy('warnForDeprecatedSplashScreen')
.and.returnValue(Promise.resolve()));
prepare.__set__('updateFileResources', jasmine.createSpy('updateFileResources').and.returnValue(Promise.resolve()));
prepare.__set__('updateConfigFilesFrom',
jasmine.createSpy('updateConfigFilesFrom')
.and.returnValue(cordovaProject.projectConfig
));
prepare.__set__('glob', {
sync: jasmine.createSpy('sync').and.returnValue({
filter: jasmine.createSpy('filter').and.returnValue([
initialJavaActivityPath
])
})
});
// prepare.__set__('events', {
// emit: function () {
// console.log(arguments);
// }
// });
spyOn(GradlePropertiesParser.prototype, 'configure');
replaceFileContents = spyOn(utils, 'replaceFileContents');
prepare.__set__('AndroidManifest', jasmine.createSpy('AndroidManifest').and.returnValue({
getPackageId: () => packageName,
getActivity: jasmine.createSpy('getActivity').and.returnValue({
setOrientation: jasmine.createSpy('setOrientation').and.returnValue({
setLaunchMode: jasmine.createSpy('setLaunchValue')
})
}),
setVersionName: jasmine.createSpy('setVersionName').and.returnValue({
setVersionCode: jasmine.createSpy('setVersionCode').and.returnValue({
write: jasmine.createSpy('write')
})
})
}));
prepare.__set__('xmlHelpers', {
parseElementtreeSync: jasmine.createSpy('parseElementtreeSync').and.returnValue(et.parse(`<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- App label shown within list of installed apps, battery & network usage screens. -->
<string name="app_name">__NAME__</string>
<!-- App label shown on the launcher. -->
<string name="launcher_name">@string/app_name</string>
<!-- App label shown on the task switcher. -->
<string name="activity_name">@string/launcher_name</string>
</resources>
`))
});
ensureDirSyncSpy = jasmine.createSpy('ensureDirSync');
copySyncSpy = jasmine.createSpy('copySync');
removeSyncSpy = jasmine.createSpy('removeSync');
prepare.__set__('fs', {
writeFileSync: jasmine.createSpy('writeFileSync'),
writeJSONSync: jasmine.createSpy('writeJSONSync'),
ensureDirSync: ensureDirSyncSpy,
copySync: copySyncSpy,
removeSync: removeSyncSpy,
existsSync: jasmine.createSpy('existsSync')
});
// The emit message was.
const actual = emitSpy.calls.argsFor(0)[1];
const expected = 'This app does not have splash screens defined';
expect(actual).toEqual(expected);
});
it('moves main activity class java file to path that tracks the package name when package name changed', async () => {
packageName = 'com.company.renamed';
const renamedPath = path.join(api.locations.javaSrc, packageName.replace(/\./g, '/'));
const renamedJavaActivityPath = path.join(renamedPath, 'MainActivity.java');
it('Test#02 : Should update splash png icon.', function () {
const updateSplashes = prepare.__get__('updateSplashes');
await api.prepare(cordovaProject, options).then(() => {
expect(replaceFileContents).toHaveBeenCalledWith(renamedJavaActivityPath, /package [\w.]*;/, 'package ' + packageName + ';');
expect(ensureDirSyncSpy).toHaveBeenCalledWith(renamedPath);
expect(copySyncSpy).toHaveBeenCalledWith(initialJavaActivityPath, renamedJavaActivityPath);
expect(removeSyncSpy).toHaveBeenCalledWith(initialJavaActivityPath);
});
// mock data.
cordovaProject.projectConfig.getSplashScreens = function (platform) {
return [mockGetSplashScreenItem({
density: 'mdpi',
src: 'res/splash/android/mdpi-screen.png'
})];
};
updateSplashes(cordovaProject, platformResourcesDir);
// The emit was called
expect(emitSpy).toHaveBeenCalled();
// The emit message was.
const actual = emitSpy.calls.argsFor(0)[1];
const expected = 'Updating splash screens at ' + PATH_RESOURCE;
expect(actual).toEqual(expected);
const actualResourceMap = updatePathsSpy.calls.argsFor(0)[0];
const expectedResourceMap = {};
expectedResourceMap[path.join(PATH_RESOURCE, 'drawable-mdpi', 'screen.png')] = 'res/splash/android/mdpi-screen.png';
expectedResourceMap[path.join(PATH_RESOURCE, 'drawable-mdpi', 'screen.webp')] = null;
expect(actualResourceMap).toEqual(expectedResourceMap);
});
it('doesn\'t move main activity class java file when package name not changed', async () => {
packageName = 'com.company.product';
it('Test#03 : Should update splash webp icon.', function () {
const updateSplashes = prepare.__get__('updateSplashes');
await api.prepare(cordovaProject, options).then(() => {
expect(replaceFileContents).toHaveBeenCalledTimes(0);
expect(ensureDirSyncSpy).toHaveBeenCalledTimes(0);
expect(copySyncSpy).toHaveBeenCalledTimes(0);
expect(removeSyncSpy).toHaveBeenCalledTimes(0);
});
// mock data.
cordovaProject.projectConfig.getSplashScreens = function (platform) {
return [mockGetSplashScreenItem({
density: 'mdpi',
src: 'res/splash/android/mdpi-screen.webp'
})];
};
// Creating Spies
updateSplashes(cordovaProject, platformResourcesDir);
// The emit was called
expect(emitSpy).toHaveBeenCalled();
// The emit message was.
const actual = emitSpy.calls.argsFor(0)[1];
const expected = 'Updating splash screens at ' + PATH_RESOURCE;
expect(actual).toEqual(expected);
const actualResourceMap = updatePathsSpy.calls.argsFor(0)[0];
const expectedResourceMap = {};
expectedResourceMap[path.join(PATH_RESOURCE, 'drawable-mdpi', 'screen.webp')] = 'res/splash/android/mdpi-screen.webp';
expectedResourceMap[path.join(PATH_RESOURCE, 'drawable-mdpi', 'screen.png')] = null;
expect(actualResourceMap).toEqual(expectedResourceMap);
});
});
});

View File

@@ -19,19 +19,10 @@
const rewire = require('rewire');
const builders = require('../../lib/builders/builders');
const MockCordovaGradleConfigParser = require('./mocks/config/MockCordovaGradleConfigParser');
const CordovaGradleConfigParser = require('../../lib/config/CordovaGradleConfigParser');
const CordovaGradleConfigParserFactory = require('../../lib/config/CordovaGradleConfigParserFactory');
describe('run', () => {
let run;
const PROJECT_DIR = 'platforms/android';
beforeAll(() => {
spyOn(CordovaGradleConfigParserFactory, 'create').and.returnValue(new MockCordovaGradleConfigParser(PROJECT_DIR));
});
beforeEach(() => {
run = rewire('../../lib/run');
run.__set__({
@@ -93,8 +84,7 @@ describe('run', () => {
buildResults: {
buildType: 'debug',
apkPaths: ['fake.apk']
},
cordovaGradleConfigParser: jasmine.any(CordovaGradleConfigParser)
}
}
);
});

View File

@@ -19,18 +19,10 @@
const rewire = require('rewire');
const { CordovaError } = require('cordova-common');
const MockCordovaGradleConfigParser = require('./mocks/config/MockCordovaGradleConfigParser');
const CordovaGradleConfigParserFactory = require('../../lib/config/CordovaGradleConfigParserFactory');
describe('target', () => {
let target;
const PROJECT_DIR = 'platforms/android';
beforeAll(() => {
spyOn(CordovaGradleConfigParserFactory, 'create').and.returnValue(new MockCordovaGradleConfigParser(PROJECT_DIR));
});
beforeEach(() => {
target = rewire('../../lib/target');
});
@@ -236,18 +228,14 @@ describe('target', () => {
describe('install', () => {
let AdbSpy;
let buildSpy;
let installTarget, manifest, cordovaGradleConfigParser, appSpec;
let installTarget, manifest, appSpec;
beforeEach(() => {
installTarget = { id: 'emulator-5556', type: 'emulator', arch: 'atari' };
manifest = jasmine.createSpyObj('manifestStub', ['getActivity']);
manifest = jasmine.createSpyObj('manifestStub', ['getPackageId', 'getActivity']);
manifest.getActivity.and.returnValue(jasmine.createSpyObj('Activity', ['getName']));
cordovaGradleConfigParser = jasmine.createSpyObj('cordovaGradleConfigParserStub', ['getPackageName']);
cordovaGradleConfigParser.getPackageName.and.returnValue('unittestapp');
appSpec = { manifest, buildResults: {}, cordovaGradleConfigParser };
appSpec = { manifest, buildResults: {} };
buildSpy = jasmine.createSpyObj('build', ['findBestApkForArchitecture']);
target.__set__('build', buildSpy);
@@ -279,7 +267,7 @@ describe('target', () => {
const apkPath = 'my/apk/path/app.apk';
buildSpy.findBestApkForArchitecture.and.returnValue(apkPath);
return target.install(installTarget, { manifest, buildResults, cordovaGradleConfigParser: CordovaGradleConfigParserFactory.create(PROJECT_DIR) }).then(() => {
return target.install(installTarget, { manifest, buildResults }).then(() => {
expect(buildSpy.findBestApkForArchitecture).toHaveBeenCalledWith(buildResults, installTarget.arch);
expect(AdbSpy.install.calls.argsFor(0)[1]).toBe(apkPath);
@@ -320,7 +308,7 @@ describe('target', () => {
it('should start the newly installed app on the device', () => {
const packageId = 'unittestapp';
const activityName = 'TestActivity';
cordovaGradleConfigParser.getPackageName.and.returnValue(packageId);
manifest.getPackageId.and.returnValue(packageId);
manifest.getActivity().getName.and.returnValue(activityName);
return target.install(installTarget, appSpec).then(() => {

View File

@@ -19,7 +19,7 @@
under the License.
*/
const android_sdk = require('cordova-android/lib/android_sdk');
var android_sdk = require('cordova-android/lib/android_sdk');
android_sdk.print_newest_available_sdk_target().catch(err => {
console.error(err);

View File

@@ -19,7 +19,7 @@
under the License.
*/
const emulators = require('cordova-android/lib/emulator');
var emulators = require('cordova-android/lib/emulator');
// Usage support for when args are given
require('cordova-android/lib/check_reqs').check_android().then(function () {

View File

@@ -20,7 +20,6 @@
package __ID__;
import android.os.Bundle;
import org.apache.cordova.*;
public class __ACTIVITY__ extends CordovaActivity

View File

@@ -18,9 +18,7 @@
under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionName="1.0"
android:versionCode="1"
android:hardwareAccelerated="true">
package="__PACKAGE__" android:versionName="1.0" android:versionCode="1" android:hardwareAccelerated="true">
<supports-screens
android:largeScreens="true"
android:normalScreens="true"
@@ -37,10 +35,9 @@
<activity android:name="__ACTIVITY__"
android:label="@string/activity_name"
android:launchMode="singleTop"
android:theme="@style/Theme.App.SplashScreen"
android:theme="@style/Theme.AppCompat.NoActionBar"
android:windowSoftInputMode="adjustResize"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
android:exported="true">
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode">
<intent-filter android:label="@string/launcher_name">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

View File

@@ -179,18 +179,16 @@ task cdvPrintProps {
}
android {
namespace cordovaConfig.PACKAGE_NAMESPACE
defaultConfig {
versionCode cdvVersionCode ?: new BigInteger("" + privateHelpers.extractIntFromManifest("versionCode"))
applicationId cordovaConfig.PACKAGE_NAMESPACE
applicationId privateHelpers.extractStringFromManifest("package")
minSdkVersion cordovaConfig.MIN_SDK_VERSION
if (cordovaConfig.MAX_SDK_VERSION != null) {
maxSdkVersion cordovaConfig.MAX_SDK_VERSION
}
targetSdkVersion cordovaConfig.SDK_VERSION
compileSdkVersion cordovaConfig.COMPILE_SDK_VERSION
compileSdkVersion cordovaConfig.SDK_VERSION
}
lintOptions {
@@ -289,7 +287,6 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: '*.jar')
implementation "androidx.appcompat:appcompat:${cordovaConfig.ANDROIDX_APP_COMPAT_VERSION}"
implementation "androidx.core:core-splashscreen:${cordovaConfig.ANDROIDX_CORE_SPLASHSCREEN_VERSION}"
if (cordovaConfig.IS_GRADLE_PLUGIN_KOTLIN_ENABLED) {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${cordovaConfig.KOTLIN_VERSION}"

View File

@@ -27,8 +27,6 @@ buildscript {
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
cdvHelpers.verifyCordovaConfigForBuild()
}
allprojects {

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

View File

@@ -1,34 +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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="512dp"
android:height="512dp"
android:viewportWidth="512"
android:viewportHeight="512">
<group
android:pivotX="243"
android:pivotY="256"
android:scaleX="0.5"
android:scaleY="0.5">
<path
android:fillColor="#9D9D9C"
android:fillType="evenOdd"
android:pathData="M425.22,448h-62.16l4.34,-54.86h-30.54L332.52,448H203.68l-4.34,-54.86H168.8l4.34,54.86h-62.16L76.1,210.22L163.38,64h209.44l87.28,146.22L425.22,448zM355.29,137.21h-56.02l3.79,27.43h-69.93l3.79,-27.43h-56.02l-35.06,73.02l17.53,146.22h209.44l17.53,-146.22L355.29,137.21zM324.75,308.02c-4.88,0 -8.67,-15.13 -8.67,-34.05s3.98,-34.05 8.67,-34.05c4.88,0 8.67,15.13 8.67,34.05S329.63,308.02 324.75,308.02zM214.7,310.86c-4.88,0 -8.67,-15.13 -8.67,-34.05s3.98,-34.05 8.67,-34.05c4.88,0 8.67,15.13 8.67,34.05S219.4,310.86 214.7,310.86z" />
</group>
</vector>

View File

@@ -2,5 +2,4 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_background" />
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
<monochrome android:drawable="@mipmap/ic_launcher_monochrome" />
</adaptive-icon>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -2,5 +2,4 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_background" />
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
<monochrome android:drawable="@mipmap/ic_launcher_monochrome" />
</adaptive-icon>
</adaptive-icon>

View File

@@ -2,5 +2,4 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_background" />
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
<monochrome android:drawable="@mipmap/ic_launcher_monochrome" />
</adaptive-icon>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 916 B

View File

@@ -2,5 +2,4 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_background" />
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
<monochrome android:drawable="@mipmap/ic_launcher_monochrome" />
</adaptive-icon>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -2,5 +2,4 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_background" />
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
<monochrome android:drawable="@mipmap/ic_launcher_monochrome" />
</adaptive-icon>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -2,5 +2,4 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_background" />
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
<monochrome android:drawable="@mipmap/ic_launcher_monochrome" />
</adaptive-icon>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<resources xmlns:tools="http://schemas.android.com/tools">
<color name="cdv_splashscreen_background">#FFFFFFFF</color>
</resources>

View File

@@ -1,22 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<resources>
<!-- App label shown within list of installed apps, battery & network usage screens. -->
<string name="app_name">__NAME__</string>

View File

@@ -1,34 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<resources>
<style name="Theme.App.SplashScreen" parent="Theme.SplashScreen.IconBackground">
<!-- Optional: Set the splash screen background. (Default: #FFFFFF) -->
<item name="windowSplashScreenBackground">@color/cdv_splashscreen_background</item>
<!-- Required: Add either a drawable or an animated drawable -->
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_cdv_splashscreen</item>
<!-- Required: For animated icons -->
<item name="windowSplashScreenAnimationDuration">200</item>
<!-- Required: Set the theme of the Activity that directly follows your splash screen. -->
<item name="postSplashScreenTheme">@style/Theme.AppCompat.NoActionBar</item>
</style>
</resources>

View File

@@ -49,6 +49,7 @@
<preference name="loglevel" value="DEBUG" />
<!--
<preference name="splashscreen" value="splash" />
<preference name="backgroundColor" value="0xFFF" />
<preference name="loadUrlTimeoutValue" value="20000" />
<preference name="InAppBrowserStorageEnabled" value="true" />

View File

@@ -21,11 +21,9 @@ apply plugin: 'com.android.application'
apply from: '../../../framework/cordova.gradle'
android {
compileSdkVersion cordovaConfig.COMPILE_SDK_VERSION
compileSdkVersion cordovaConfig.SDK_VERSION
buildToolsVersion cordovaConfig.BUILD_TOOLS_VERSION
namespace 'org.apache.cordova.unittests'
defaultConfig {
applicationId "org.apache.cordova.unittests"
minSdkVersion cordovaConfig.MIN_SDK_VERSION
@@ -58,6 +56,8 @@ dependencies {
androidTestImplementation('androidx.test.espresso:espresso-web:3.1.1', {
exclude group: 'androidx.test.espresso', module: 'androidx.annotation'
})
androidTestImplementation 'androidx.test:rules:1.4.0'
}
repositories {
google()

View File

@@ -158,7 +158,7 @@ public class BackButtonMultipageTest {
assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
}
private void assertPageSample(String url) {
private void assertPageSample(String url) throws Throwable {
assertEquals(url, mActivity.onPageFinishedUrl.take());
}
}

View File

@@ -17,7 +17,8 @@
specific language governing permissions and limitations
under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.apache.cordova.unittests">
<application
android:allowBackup="true"
@@ -28,8 +29,7 @@
<activity android:name=".EmbeddedWebViewActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name"
android:windowSoftInputMode="adjustPan"
android:exported="true">
android:windowSoftInputMode="adjustPan">
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -38,8 +38,7 @@
<activity android:name=".StandardActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name"
android:windowSoftInputMode="adjustPan"
android:exported="true">
android:windowSoftInputMode="adjustPan">
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -48,8 +47,7 @@
<activity android:name=".TestActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name"
android:windowSoftInputMode="adjustPan"
android:exported="true">
android:windowSoftInputMode="adjustPan">
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

View File

@@ -21,7 +21,7 @@ const fs = require('fs-extra');
const path = require('path');
/**
* This script is to be run manually (e.g. by npm run clean:java-unit-tests) if
* This script is to be run manually (e.g. by npm run clean:java-tests) if
* you want to upgrade gradlew or test its proper generation.
*/

View File

@@ -46,9 +46,13 @@ class AndroidTestRunner {
return new ProjectBuilder(this.projectDir).runGradleWrapper('gradle');
}
_log (...args) {
console.log(...[`[${this.testTitle}]`, ...args]);
}
run () {
return Promise.resolve()
.then(_ => console.log(`[${this.testTitle}] Preparing Gradle wrapper for Java unit tests.`))
.then(_ => this._log('Staging Project Files'))
.then(_ => {
// TODO we should probably not only copy these files, but instead create a new project from scratch
fs.copyFileSync(path.resolve(this.projectDir, '../../framework/cdv-gradle-config-defaults.json'), path.resolve(this.projectDir, 'cdv-gradle-config.json'));
@@ -57,26 +61,35 @@ class AndroidTestRunner {
path.join(this.projectDir, 'app/src/main/assets/www/cordova.js')
);
})
.then(_ => this._log('Creating Gradle Wrapper'))
.then(_ => this._createProjectBuilder())
.then(_ => this._log('Getting Gradle Wrapper Version Info'))
.then(_ => this._gradlew('--version'))
.then(_ => console.log(`[${this.testTitle}] Gradle wrapper is ready. Running tests now.`))
.then(_ => this._log('Running Java Unit Tests'))
.then(_ => this._gradlew('test'))
.then(_ => console.log(`[${this.testTitle}] Java unit tests completed successfully`));
.then(_ => this._log('Finished Java Unit Test'))
.then(_ => this._log('Running Java Instrumentation Tests'))
.then(_ => this._gradlew('connectedAndroidTest'))
.then(_ => this._log('Finished Java Instrumentation Tests'));
}
}
Promise.resolve()
.then(_ => console.log('Starting to run all android platform tests'))
.then(_ => console.log('Starting Android Platform Java Tests'))
// AndroidX Test
.then(_ => new AndroidTestRunner('AndroidX Project', path.resolve(__dirname, 'androidx')))
.then(test => test.run())
.then(_ => console.log('Finished running all android platform tests'));
.then(_ => console.log('Finished Running Android Platform Java Tests'));
process.on('unhandledRejection', err => {
// If err has a stderr property, we have seen the message already
if (!('stderr' in err)) console.error(err.message);
console.error('JAVA UNIT TESTS FAILED!');
console.error('JAVA TESTS FAILED!');
process.exitCode = err.code || 1;
});

26
types/index.d.ts vendored
View File

@@ -1,26 +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.
*/
interface Navigator {
/** This plugin displays and hides a splash screen during application launch. */
splashscreen: {
/** Dismiss the splash screen. */
hide(): void;
}
}