mirror of
https://github.com/apache/cordova-android.git
synced 2026-04-04 00:02:03 +08:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c805a0a8e | ||
|
|
a7cd4227a4 | ||
|
|
c9e7c59986 | ||
|
|
94234d988e | ||
|
|
b104554877 | ||
|
|
7da13ccf77 | ||
|
|
cb48147398 | ||
|
|
6f6717afbd | ||
|
|
3343c3bb34 | ||
|
|
a62f699380 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x, 18.x]
|
||||
node-version: [16.x, 18.x, 20.x]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
|
||||
steps:
|
||||
|
||||
@@ -20,6 +20,65 @@
|
||||
-->
|
||||
## Release Notes for Cordova (Android)
|
||||
|
||||
### 12.0.1 (Aug 23, 2023)
|
||||
|
||||
* [GH-1632](https://github.com/apache/cordova-android/pull/1632) fix(android): `monochrome` checks
|
||||
* [GH-1649](https://github.com/apache/cordova-android/pull/1649) chore: rebuild `package-lock` w/ lint corrections
|
||||
|
||||
### 12.0.0 (May 20, 2023)
|
||||
|
||||
**Breaking:**
|
||||
|
||||
* [GH-1605](https://github.com/apache/cordova-android/pull/1605) fix!: Make `CoreAndroid` plugin instantiate on load
|
||||
* [GH-1539](https://github.com/apache/cordova-android/pull/1539) feat!: bump Gradle 7.6 & AGP 7.4.2
|
||||
* [GH-1571](https://github.com/apache/cordova-android/pull/1571) feat!: bump min SDK to 24
|
||||
* [GH-1538](https://github.com/apache/cordova-android/pull/1538) feat!: bump target sdk & build tools for SDK 33 support
|
||||
* [GH-1540](https://github.com/apache/cordova-android/pull/1540) feat!: bump node engine requirement `>=16.13.0`
|
||||
* [GH-1597](https://github.com/apache/cordova-android/pull/1597) deprecate: `CoreAndroid.getBuildConfigValue`
|
||||
* [GH-1541](https://github.com/apache/cordova-android/pull/1541) dep(npm)!: bump acceptable modules w/ rebuilt `package-lock`
|
||||
* [GH-1566](https://github.com/apache/cordova-android/pull/1566) dep(npm)!: bump `cordova-common@5.0.0`
|
||||
|
||||
**Features:**
|
||||
|
||||
* [GH-1602](https://github.com/apache/cordova-android/pull/1602) feat: add `listTarget` api
|
||||
* [GH-1574](https://github.com/apache/cordova-android/pull/1574) feat: add plugin hooks for `WebViewClient.onRenderProcessGone`
|
||||
* [GH-1594](https://github.com/apache/cordova-android/pull/1594) feat: bump default `kotlin` to version 1.7.21
|
||||
* [GH-1550](https://github.com/apache/cordova-android/pull/1550) feat: add `monochrome` app icon support
|
||||
* [GH-1589](https://github.com/apache/cordova-android/pull/1589) feat: `InspectableWebview` preference
|
||||
* [GH-1568](https://github.com/apache/cordova-android/pull/1568) feat: bump `androidx.appcompat.appcompat` 1.6.1
|
||||
* [GH-1567](https://github.com/apache/cordova-android/pull/1567) feat: bump `androidx.webkit.webkit` 1.6.0
|
||||
* [GH-1545](https://github.com/apache/cordova-android/pull/1545) feat: bump `androidx.webkit.webkit` 1.5.0
|
||||
* [GH-1547](https://github.com/apache/cordova-android/pull/1547) feat: bump `com.google.gms.google-services` 4.3.15
|
||||
* [GH-1546](https://github.com/apache/cordova-android/pull/1546) feat: bump `androidx.core.core-splashscreen` 1.0.0
|
||||
* [GH-1544](https://github.com/apache/cordova-android/pull/1544) feat: bump `androidx.appcompat.appcompat` 1.5.1
|
||||
|
||||
**Fixes:**
|
||||
|
||||
* [GH-1606](https://github.com/apache/cordova-android/pull/1606) fix: Gradle Args parsing
|
||||
* [GH-1575](https://github.com/apache/cordova-android/pull/1575) fix(`BuildHelper`): get package name from `ApplicationInfo`
|
||||
* [GH-1595](https://github.com/apache/cordova-android/pull/1595) fix(test): Native test namespace refactor
|
||||
* [GH-1471](https://github.com/apache/cordova-android/pull/1471) fix: `ANDROID_HOME` is the new default, to check first and give advice
|
||||
* [GH-1573](https://github.com/apache/cordova-android/pull/1573) fix(GH-1432): Default `content` `src` when content tag is missing
|
||||
* [GH-1506](https://github.com/apache/cordova-android/pull/1506) fix: only do fadeout animation if `FadeSplashScreen` is true
|
||||
* [GH-1505](https://github.com/apache/cordova-android/pull/1505) fix: correctly flag API dependency on `AppCompat` for Maven
|
||||
* [GH-1487](https://github.com/apache/cordova-android/pull/1487) fix: Add **Android** prefix to `WindowSplashScreenBrandingImage`
|
||||
* [GH-1489](https://github.com/apache/cordova-android/pull/1489) fix: import type definitions from obsolete `cordova-plugin-splashscreen`
|
||||
|
||||
**Chores, Refactor, Dependencies & CI:**
|
||||
|
||||
* [GH-1493](https://github.com/apache/cordova-android/pull/1493) chore: add `lint:fix` script for fixing lint errors
|
||||
* [GH-1491](https://github.com/apache/cordova-android/pull/1491) chore: Use gradle 7.4.2 distribution url
|
||||
* [GH-1588](https://github.com/apache/cordova-android/pull/1588) refactor: Removed obsolete version code checks
|
||||
* [GH-1492](https://github.com/apache/cordova-android/pull/1492) refactor: replace deprecated `Handler` constructor
|
||||
* [GH-1587](https://github.com/apache/cordova-android/pull/1587) dep: bump npm dependencies
|
||||
* `fs-extra@11.1.1`
|
||||
* `nopt@7.1.0`
|
||||
* `@cordova/eslint-config@5.0.0`
|
||||
* `jasmine@4.6.0`
|
||||
* [GH-1607](https://github.com/apache/cordova-android/pull/1607) ci: Added NodeJS 20.x to the workflow matrix
|
||||
* [GH-1542](https://github.com/apache/cordova-android/pull/1542) ci(workflow): update `codecov/codecov-action@v3`
|
||||
* [GH-1532](https://github.com/apache/cordova-android/pull/1532) ci: update `codecov/codecov-action` reporting format
|
||||
|
||||
### 11.0.0 (Jul 04, 2022)
|
||||
|
||||
**Breaking:**
|
||||
|
||||
@@ -31,6 +31,8 @@ import android.content.res.Configuration;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.webkit.RenderProcessGoneDetail;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
@@ -442,4 +444,19 @@ public class CordovaPlugin {
|
||||
public CordovaPluginPathHandler getPathHandler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the WebView's render process has exited. Can be used to collect information regarding the crash for crashlytics or optionally attempt to gracefully handle/recover the crashed webview by recreating it.
|
||||
*
|
||||
* See <a href="https://developer.android.com/reference/android/webkit/WebViewClient#onRenderProcessGone(android.webkit.WebView,%20android.webkit.RenderProcessGoneDetail)">WebViewClient#onRenderProcessGone</a>
|
||||
*
|
||||
* Note: A plugin must not attempt to recover a webview that it does not own/manage.
|
||||
*
|
||||
* @return true if the host application handled the situation that process has exited,
|
||||
* otherwise, application will crash if render process crashed, or be killed
|
||||
* if render process was killed by the system.
|
||||
*/
|
||||
public boolean onRenderProcessGone(final WebView view, RenderProcessGoneDetail detail) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 = "12.0.1";
|
||||
|
||||
void init(CordovaInterface cordova, List<PluginEntry> pluginEntries, CordovaPreferences preferences);
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ public class CordovaWebViewImpl implements CordovaWebView {
|
||||
// This isn't enforced by the compiler, so assert here.
|
||||
assert engine.getView() instanceof CordovaWebViewEngine.EngineView;
|
||||
|
||||
pluginManager.addService(CoreAndroid.PLUGIN_NAME, "org.apache.cordova.CoreAndroid");
|
||||
pluginManager.addService(CoreAndroid.PLUGIN_NAME, "org.apache.cordova.CoreAndroid", true);
|
||||
pluginManager.init();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Debug;
|
||||
import android.os.Build;
|
||||
import android.webkit.RenderProcessGoneDetail;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
* PluginManager is exposed to JavaScript in the Cordova WebView.
|
||||
@@ -197,7 +199,18 @@ public class PluginManager {
|
||||
* @param className The plugin class name
|
||||
*/
|
||||
public void addService(String service, String className) {
|
||||
PluginEntry entry = new PluginEntry(service, className, false);
|
||||
addService(service, className, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a plugin class that implements a service to the service entry table.
|
||||
*
|
||||
* @param service The service name
|
||||
* @param className The plugin class name
|
||||
* @param onload If true, the plugin will be instantiated immediately
|
||||
*/
|
||||
public void addService(String service, String className, boolean onload) {
|
||||
PluginEntry entry = new PluginEntry(service, className, onload);
|
||||
this.addService(entry);
|
||||
}
|
||||
|
||||
@@ -606,4 +619,29 @@ public class PluginManager {
|
||||
}
|
||||
return handlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the WebView's render process has exited.
|
||||
*
|
||||
* See https://developer.android.com/reference/android/webkit/WebViewClient#onRenderProcessGone(android.webkit.WebView,%20android.webkit.RenderProcessGoneDetail)
|
||||
*
|
||||
* @return true if the host application handled the situation that process has exited,
|
||||
* otherwise, application will crash if render process crashed, or be killed
|
||||
* if render process was killed by the system.
|
||||
*/
|
||||
public boolean onRenderProcessGone(final WebView view, RenderProcessGoneDetail detail) {
|
||||
boolean result = false;
|
||||
synchronized (this.entryMap) {
|
||||
for (PluginEntry entry : this.entryMap.values()) {
|
||||
CordovaPlugin plugin = pluginMap.get(entry.service);
|
||||
if (plugin != null) {
|
||||
if (plugin.onRenderProcessGone(view, detail)) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.net.http.SslError;
|
||||
import android.webkit.ClientCertRequest;
|
||||
import android.webkit.HttpAuthHandler;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.webkit.RenderProcessGoneDetail;
|
||||
import android.webkit.SslErrorHandler;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebResourceResponse;
|
||||
@@ -422,4 +423,15 @@ public class SystemWebViewClient extends WebViewClient {
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
|
||||
return this.assetLoader.shouldInterceptRequest(request.getUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRenderProcessGone(final WebView view, RenderProcessGoneDetail detail) {
|
||||
// Check if there is some plugin which can handle this event
|
||||
PluginManager pluginManager = this.parentEngine.pluginManager;
|
||||
if (pluginManager != null && pluginManager.onRenderProcessGone(view, detail)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onRenderProcessGone(view, detail);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,6 +277,12 @@ class Api {
|
||||
});
|
||||
}
|
||||
|
||||
listTargets (options) {
|
||||
return require('./check_reqs').check_android().then(() => {
|
||||
return require('./run').runListDevices.call(this, options);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans out the build artifacts from platform's directory, and also
|
||||
* cleans out the platform www directory if called without options specified.
|
||||
|
||||
@@ -21,6 +21,7 @@ const path = require('path');
|
||||
const fs = require('fs');
|
||||
const nopt = require('nopt');
|
||||
const untildify = require('untildify');
|
||||
const { parseArgsStringToArgv } = require('string-argv');
|
||||
|
||||
const Adb = require('./Adb');
|
||||
|
||||
@@ -36,7 +37,11 @@ function parseOpts (options, resolvedTarget, projectRoot) {
|
||||
minSdkVersion: String,
|
||||
maxSdkVersion: String,
|
||||
targetSdkVersion: String,
|
||||
|
||||
// This needs to be an array since nopts will parse its entries as further options for this process
|
||||
// It will be an array of 1 string: [ "string args" ]
|
||||
gradleArg: [String, Array],
|
||||
|
||||
keystore: path,
|
||||
alias: String,
|
||||
storePassword: String,
|
||||
@@ -58,7 +63,8 @@ function parseOpts (options, resolvedTarget, projectRoot) {
|
||||
if (options.argv.maxSdkVersion) { ret.extraArgs.push('-PcdvMaxSdkVersion=' + options.argv.maxSdkVersion); }
|
||||
if (options.argv.targetSdkVersion) { ret.extraArgs.push('-PcdvTargetSdkVersion=' + options.argv.targetSdkVersion); }
|
||||
if (options.argv.gradleArg) {
|
||||
ret.extraArgs = ret.extraArgs.concat(options.argv.gradleArg);
|
||||
const gradleArgs = parseArgsStringToArgv(options.argv.gradleArg[0]);
|
||||
ret.extraArgs = ret.extraArgs.concat(gradleArgs);
|
||||
}
|
||||
|
||||
const packageArgs = {};
|
||||
|
||||
@@ -85,7 +85,13 @@ class ProjectBuilder {
|
||||
}
|
||||
|
||||
getArgs (cmd, opts) {
|
||||
let args;
|
||||
let args = [
|
||||
'-b', path.join(this.root, 'build.gradle')
|
||||
];
|
||||
if (opts.extraArgs) {
|
||||
args = args.concat(opts.extraArgs);
|
||||
}
|
||||
|
||||
let buildCmd = cmd;
|
||||
if (opts.packageType === PackageType.BUNDLE) {
|
||||
if (cmd === 'release') {
|
||||
@@ -93,8 +99,6 @@ class ProjectBuilder {
|
||||
} else if (cmd === 'debug') {
|
||||
buildCmd = ':app:bundleDebug';
|
||||
}
|
||||
|
||||
args = [buildCmd, '-b', path.join(this.root, 'build.gradle')];
|
||||
} else {
|
||||
if (cmd === 'release') {
|
||||
buildCmd = 'cdvBuildRelease';
|
||||
@@ -102,14 +106,12 @@ class ProjectBuilder {
|
||||
buildCmd = 'cdvBuildDebug';
|
||||
}
|
||||
|
||||
args = [buildCmd, '-b', path.join(this.root, 'build.gradle')];
|
||||
|
||||
if (opts.arch) {
|
||||
args.push('-PcdvBuildArch=' + opts.arch);
|
||||
}
|
||||
}
|
||||
|
||||
args.push.apply(args, opts.extraArgs);
|
||||
args.push(buildCmd);
|
||||
|
||||
return args;
|
||||
}
|
||||
@@ -318,6 +320,8 @@ class ProjectBuilder {
|
||||
const wrapper = path.join(this.root, 'gradlew');
|
||||
const args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
|
||||
|
||||
events.emit('verbose', `Running Gradle Build: ${wrapper} ${args.join(' ')}`);
|
||||
|
||||
try {
|
||||
return await execa(wrapper, args, { stdio: 'inherit', cwd: path.resolve(this.root) });
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
|
||||
@@ -21,6 +21,7 @@ const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const nopt = require('nopt');
|
||||
const glob = require('fast-glob');
|
||||
const dedent = require('dedent');
|
||||
const events = require('cordova-common').events;
|
||||
const AndroidManifest = require('./AndroidManifest');
|
||||
const xmlHelpers = require('cordova-common').xmlHelpers;
|
||||
@@ -747,9 +748,24 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
|
||||
foreground = android_icons[density].foreground;
|
||||
monochrome = android_icons[density].monochrome;
|
||||
|
||||
const isAdaptiveIcon = background && foreground;
|
||||
const isMonochromeIcon = monochrome && isAdaptiveIcon;
|
||||
if (!isMonochromeIcon || !isAdaptiveIcon) {
|
||||
const hasAdaptiveIcons = !!background && !!foreground;
|
||||
let hasMonochromeIcon = !!monochrome;
|
||||
|
||||
if (hasMonochromeIcon && !hasAdaptiveIcons) {
|
||||
// If we have a monochrome icon, but no adaptive icons,
|
||||
// then warn that in order to use monochrome, the adaptive icons
|
||||
// must be supplied. We will ignore monochrome and proceed with the
|
||||
// icon preparation however.
|
||||
hasMonochromeIcon = false;
|
||||
monochrome = undefined;
|
||||
events.emit('warn', dedent`
|
||||
Monochrome icon found but without adaptive properties.
|
||||
Monochrome icon requires the adaptive background and foreground assets.
|
||||
See https://cordova.apache.org/docs/en/latest/config_ref/images.html fore more information.
|
||||
`);
|
||||
}
|
||||
|
||||
if (!hasAdaptiveIcons) {
|
||||
// This icon isn't an adaptive icon, so skip it
|
||||
continue;
|
||||
}
|
||||
@@ -780,7 +796,7 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
|
||||
resourceMap[targetPathForeground] = android_icons[density].foreground;
|
||||
}
|
||||
|
||||
if (monochrome) {
|
||||
if (hasMonochromeIcon) {
|
||||
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));
|
||||
@@ -794,19 +810,23 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
|
||||
|
||||
// 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>`;
|
||||
if (hasMonochromeIcon) {
|
||||
icLauncherTemplate = dedent`
|
||||
<?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"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="` + backgroundVal + `" />
|
||||
<foreground android:drawable="` + foregroundVal + `" />
|
||||
</adaptive-icon>`;
|
||||
icLauncherTemplate = dedent`
|
||||
<?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');
|
||||
|
||||
46
lib/run.js
46
lib/run.js
@@ -83,3 +83,49 @@ module.exports.run = async function (runOptions = {}) {
|
||||
|
||||
return target.install(resolvedTarget, { manifest, buildResults, cordovaGradleConfigParser });
|
||||
};
|
||||
|
||||
module.exports.listDevices = async function () {
|
||||
events.emit('log', `\nAvailable ${this.platform} devices:`);
|
||||
|
||||
const { list } = require('./target');
|
||||
|
||||
await list().then(targets => {
|
||||
const deviceIds = targets
|
||||
.filter(({ type }) => type === 'device')
|
||||
.map(({ id }) => id);
|
||||
|
||||
console.log(deviceIds.join('\n'));
|
||||
}, function (err) {
|
||||
console.error('ERROR: ' + err);
|
||||
process.exit(2);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.listEmulators = async function () {
|
||||
events.emit('log', `\nAvailable ${this.platform} virtual devices:`);
|
||||
const emulators = require('./emulator');
|
||||
|
||||
await emulators.list_images().then(function (emulator_list) {
|
||||
emulator_list && emulator_list.forEach(function (emu) {
|
||||
console.log(emu.name);
|
||||
});
|
||||
}, function (err) {
|
||||
console.error('ERROR: ' + err);
|
||||
process.exit(2);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.runListDevices = async function (options = {}) {
|
||||
const { options: cliArgs = {} } = options;
|
||||
|
||||
if (cliArgs?.device) {
|
||||
await module.exports.listDevices.call(this);
|
||||
} else if (cliArgs?.emulator) {
|
||||
await module.exports.listEmulators.call(this);
|
||||
} else {
|
||||
await module.exports.listDevices.call(this);
|
||||
await module.exports.listEmulators.call(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
1895
package-lock.json
generated
1895
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cordova-android",
|
||||
"version": "12.0.0-dev",
|
||||
"version": "12.0.1",
|
||||
"description": "cordova-android release",
|
||||
"types": "./types/index.d.ts",
|
||||
"main": "lib/Api.js",
|
||||
@@ -27,6 +27,7 @@
|
||||
"dependencies": {
|
||||
"android-versions": "^1.8.1",
|
||||
"cordova-common": "^5.0.0",
|
||||
"dedent": "^1.0.1",
|
||||
"execa": "^5.1.1",
|
||||
"fast-glob": "^3.2.12",
|
||||
"fs-extra": "^11.1.1",
|
||||
@@ -34,6 +35,7 @@
|
||||
"nopt": "^7.1.0",
|
||||
"properties-parser": "^0.3.1",
|
||||
"semver": "^7.3.8",
|
||||
"string-argv": "^0.3.1",
|
||||
"untildify": "^4.0.0",
|
||||
"which": "^3.0.0"
|
||||
},
|
||||
|
||||
@@ -24,6 +24,8 @@ const EventEmitter = require('events');
|
||||
|
||||
const Api = require('../../lib/Api');
|
||||
const AndroidProject = require('../../lib/AndroidProject');
|
||||
const check_reqs = require('../../lib/check_reqs');
|
||||
const run_mod = require('../../lib/run');
|
||||
|
||||
const PluginInfo = common.PluginInfo;
|
||||
|
||||
@@ -60,4 +62,19 @@ describe('Api', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('listTargets', () => {
|
||||
let api;
|
||||
|
||||
beforeEach(() => {
|
||||
api = new Api('android', FAKE_PROJECT_DIR, new EventEmitter());
|
||||
spyOn(check_reqs, 'run').and.returnValue(Promise.resolve());
|
||||
});
|
||||
it('should call into lib/run module', () => {
|
||||
spyOn(run_mod, 'runListDevices');
|
||||
return api.listTargets().then(() => {
|
||||
expect(run_mod.runListDevices).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
116
spec/unit/build.spec.js
Normal file
116
spec/unit/build.spec.js
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
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 rewire = require('rewire');
|
||||
const builders = require('../../lib/builders/builders');
|
||||
|
||||
describe('build', () => {
|
||||
let build;
|
||||
const builder = builders.getBuilder('FakeRootPath');
|
||||
|
||||
beforeEach(() => {
|
||||
build = rewire('../../lib/build');
|
||||
build.__set__({
|
||||
events: jasmine.createSpyObj('eventsSpy', ['emit'])
|
||||
});
|
||||
|
||||
// run needs `this` to behave like an Api instance
|
||||
build.run = build.run.bind({
|
||||
_builder: builder
|
||||
});
|
||||
|
||||
spyOn(builder, 'build').and.returnValue(Promise.resolve({
|
||||
paths: ['fake.apk'],
|
||||
buildtype: 'debug'
|
||||
}));
|
||||
});
|
||||
|
||||
describe('argument parsing', () => {
|
||||
let prepEnvSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
prepEnvSpy = spyOn(builder, 'prepEnv').and.returnValue(Promise.resolve());
|
||||
});
|
||||
|
||||
describe('gradleArg', () => {
|
||||
const baseOptions = {
|
||||
packageType: 'apk',
|
||||
arch: undefined,
|
||||
prepEnv: undefined,
|
||||
buildType: 'debug'
|
||||
};
|
||||
|
||||
it('can parse single gradle argument', async () => {
|
||||
await build.run({
|
||||
argv: [
|
||||
'node',
|
||||
'--gradleArg=--stacktrace'
|
||||
]
|
||||
});
|
||||
|
||||
expect(prepEnvSpy).toHaveBeenCalledWith({
|
||||
...baseOptions,
|
||||
extraArgs: ['--stacktrace']
|
||||
});
|
||||
});
|
||||
|
||||
it('can parse multiple gradle arguments', async () => {
|
||||
await build.run({
|
||||
argv: [
|
||||
'node',
|
||||
'--gradleArg=--stacktrace --info'
|
||||
]
|
||||
});
|
||||
|
||||
expect(prepEnvSpy).toHaveBeenCalledWith({
|
||||
...baseOptions,
|
||||
extraArgs: ['--stacktrace', '--info']
|
||||
});
|
||||
});
|
||||
|
||||
it('can parse multiple gradle arguments with strings', async () => {
|
||||
await build.run({
|
||||
argv: [
|
||||
'node',
|
||||
'--gradleArg=--testArg="hello world"'
|
||||
]
|
||||
});
|
||||
|
||||
expect(prepEnvSpy).toHaveBeenCalledWith({
|
||||
...baseOptions,
|
||||
extraArgs: ['--testArg="hello world"']
|
||||
});
|
||||
});
|
||||
|
||||
it('gradle args will split when necessary', async () => {
|
||||
await build.run({
|
||||
argv: [
|
||||
'node',
|
||||
'--gradleArg=--warning-mode all'
|
||||
]
|
||||
});
|
||||
|
||||
expect(prepEnvSpy).toHaveBeenCalledWith({
|
||||
...baseOptions,
|
||||
extraArgs: ['--warning-mode', 'all']
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -53,40 +53,40 @@ describe('ProjectBuilder', () => {
|
||||
|
||||
it('should set release argument', () => {
|
||||
const args = builder.getArgs('release', {});
|
||||
expect(args[0]).toBe('cdvBuildRelease');
|
||||
expect(args[args.length - 1]).toBe('cdvBuildRelease');
|
||||
});
|
||||
|
||||
it('should set debug argument', () => {
|
||||
const args = builder.getArgs('debug', {});
|
||||
expect(args[0]).toBe('cdvBuildDebug');
|
||||
expect(args[args.length - 1]).toBe('cdvBuildDebug');
|
||||
});
|
||||
|
||||
it('should set apk release', () => {
|
||||
const args = builder.getArgs('release', {
|
||||
packageType: 'apk'
|
||||
});
|
||||
expect(args[0]).withContext(args).toBe('cdvBuildRelease');
|
||||
expect(args[args.length - 1]).withContext(args).toBe('cdvBuildRelease');
|
||||
});
|
||||
|
||||
it('should set apk debug', () => {
|
||||
const args = builder.getArgs('debug', {
|
||||
packageType: 'apk'
|
||||
});
|
||||
expect(args[0]).withContext(args).toBe('cdvBuildDebug');
|
||||
expect(args[args.length - 1]).withContext(args).toBe('cdvBuildDebug');
|
||||
});
|
||||
|
||||
it('should set bundle release', () => {
|
||||
const args = builder.getArgs('release', {
|
||||
packageType: 'bundle'
|
||||
});
|
||||
expect(args[0]).withContext(args).toBe(':app:bundleRelease');
|
||||
expect(args[args.length - 1]).withContext(args).toBe(':app:bundleRelease');
|
||||
});
|
||||
|
||||
it('should set bundle debug', () => {
|
||||
const args = builder.getArgs('debug', {
|
||||
packageType: 'bundle'
|
||||
});
|
||||
expect(args[0]).withContext(args).toBe(':app:bundleDebug');
|
||||
expect(args[args.length - 1]).withContext(args).toBe(':app:bundleDebug');
|
||||
});
|
||||
|
||||
it('should add architecture if it is passed', () => {
|
||||
@@ -100,14 +100,14 @@ describe('ProjectBuilder', () => {
|
||||
const args = builder.getArgs('clean', {
|
||||
packageType: 'apk'
|
||||
});
|
||||
expect(args[0]).toBe('clean');
|
||||
expect(args[args.length - 1]).toBe('clean');
|
||||
});
|
||||
|
||||
it('should clean bundle', () => {
|
||||
const args = builder.getArgs('clean', {
|
||||
packageType: 'bundle'
|
||||
});
|
||||
expect(args[0]).toBe('clean');
|
||||
expect(args[args.length - 1]).toBe('clean');
|
||||
});
|
||||
|
||||
describe('should accept extra arguments', () => {
|
||||
@@ -176,6 +176,7 @@ describe('ProjectBuilder', () => {
|
||||
it('should reject if the spawn fails', () => {
|
||||
const errorMessage = 'Test error';
|
||||
execaSpy.and.rejectWith(new Error(errorMessage));
|
||||
builder.getArgs.and.returnValue([]);
|
||||
|
||||
return builder.build({}).then(
|
||||
() => fail('Unexpectedly resolved'),
|
||||
@@ -192,6 +193,7 @@ describe('ProjectBuilder', () => {
|
||||
ProjectBuilder.__set__('check_reqs', checkReqsSpy);
|
||||
checkReqsSpy.check_android_target.and.resolveTo();
|
||||
execaSpy.and.rejectWith(testError);
|
||||
builder.getArgs.and.returnValue([]);
|
||||
|
||||
return builder.build({}).then(
|
||||
() => fail('Unexpectedly resolved'),
|
||||
|
||||
@@ -630,43 +630,106 @@ describe('prepare', () => {
|
||||
let preparedIcons;
|
||||
let resourceMap;
|
||||
|
||||
beforeEach(function () {
|
||||
// Mocked Data
|
||||
platformResourcesDir = PATH_RESOURCE;
|
||||
preparedIcons = {
|
||||
android_icons: {
|
||||
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'
|
||||
})
|
||||
},
|
||||
default_icon: undefined
|
||||
};
|
||||
describe('without monochrome', () => {
|
||||
beforeEach(function () {
|
||||
// Mocked Data
|
||||
platformResourcesDir = PATH_RESOURCE;
|
||||
preparedIcons = {
|
||||
android_icons: {
|
||||
mdpi: mockGetIconItem({
|
||||
density: 'mdpi',
|
||||
background: 'res/icon/android/mdpi-background.png',
|
||||
foreground: 'res/icon/android/mdpi-foreground.png'
|
||||
})
|
||||
},
|
||||
default_icon: undefined
|
||||
};
|
||||
|
||||
resourceMap = createResourceMap();
|
||||
resourceMap = createResourceMap();
|
||||
|
||||
fsWriteFileSyncSpy = jasmine.createSpy('writeFileSync');
|
||||
prepare.__set__('fs', {
|
||||
writeFileSync: fsWriteFileSyncSpy
|
||||
fsWriteFileSyncSpy = jasmine.createSpy('writeFileSync');
|
||||
prepare.__set__('fs', {
|
||||
writeFileSync: fsWriteFileSyncSpy
|
||||
});
|
||||
});
|
||||
|
||||
it('Test#001 : Should update resource map with prepared icons.', function () {
|
||||
// Get method for testing
|
||||
const updateIconResourceForAdaptive = prepare.__get__('updateIconResourceForAdaptive');
|
||||
|
||||
// Run Test
|
||||
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';
|
||||
|
||||
const expected = Object.assign({}, resourceMap, expectedModification);
|
||||
const actual = updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
it('Test#001 : Should update resource map with prepared icons.', function () {
|
||||
// Get method for testing
|
||||
const updateIconResourceForAdaptive = prepare.__get__('updateIconResourceForAdaptive');
|
||||
describe('with monochrome', () => {
|
||||
beforeEach(function () {
|
||||
// Mocked Data
|
||||
platformResourcesDir = PATH_RESOURCE;
|
||||
preparedIcons = {
|
||||
android_icons: {
|
||||
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'
|
||||
})
|
||||
},
|
||||
default_icon: undefined
|
||||
};
|
||||
|
||||
// Run Test
|
||||
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';
|
||||
resourceMap = createResourceMap();
|
||||
|
||||
const expected = Object.assign({}, resourceMap, expectedModification);
|
||||
const actual = updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
|
||||
fsWriteFileSyncSpy = jasmine.createSpy('writeFileSync');
|
||||
prepare.__set__('fs', {
|
||||
writeFileSync: fsWriteFileSyncSpy
|
||||
});
|
||||
});
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
it('Test#002 : Should update resource map with prepared icons.', function () {
|
||||
// Get method for testing
|
||||
const updateIconResourceForAdaptive = prepare.__get__('updateIconResourceForAdaptive');
|
||||
|
||||
// Run Test
|
||||
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);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('Test#003 : should emit if monochrome is supplied without adaptive background', () => {
|
||||
const updateIconResourceForAdaptive = prepare.__get__('updateIconResourceForAdaptive');
|
||||
|
||||
preparedIcons.android_icons.mdpi.background = undefined;
|
||||
updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
|
||||
|
||||
const actualEmitArgs = emitSpy.calls.mostRecent().args;
|
||||
expect(actualEmitArgs[0]).toBe('warn');
|
||||
expect(actualEmitArgs[1]).toMatch(/Monochrome icon found but without adaptive properties./g);
|
||||
});
|
||||
|
||||
it('Test#004 : should emit if monochrome is supplied without adaptive foreground', () => {
|
||||
const updateIconResourceForAdaptive = prepare.__get__('updateIconResourceForAdaptive');
|
||||
|
||||
preparedIcons.android_icons.mdpi.foreground = undefined;
|
||||
updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
|
||||
|
||||
const actualEmitArgs = emitSpy.calls.mostRecent().args;
|
||||
expect(actualEmitArgs[0]).toBe('warn');
|
||||
expect(actualEmitArgs[1]).toMatch(/Monochrome icon found but without adaptive properties./g);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -106,4 +106,32 @@ describe('run', () => {
|
||||
.toBeRejectedWithError(/Package type "bundle" is not supported/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('--list option', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(run, 'listDevices').and.returnValue(Promise.resolve());
|
||||
spyOn(run, 'listEmulators').and.returnValue(Promise.resolve());
|
||||
});
|
||||
|
||||
it('should delegate to "listDevices" when the "runListDevices" method options param contains "options.device".', () => {
|
||||
return run.runListDevices({ options: { device: true } }).then(() => {
|
||||
expect(run.listDevices).toHaveBeenCalled();
|
||||
expect(run.listEmulators).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should delegate to "listDevices" when the "runListDevices" method options param contains "options.emulator".', () => {
|
||||
return run.runListDevices({ options: { emulator: true } }).then(() => {
|
||||
expect(run.listDevices).not.toHaveBeenCalled();
|
||||
expect(run.listEmulators).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should delegate to both "listEmulators" and "listDevices" when the "runListDevices" method does not contain "options.device" or "options.emulator".', () => {
|
||||
return run.runListDevices({ options: {} }).then(() => {
|
||||
expect(run.listDevices).toHaveBeenCalled();
|
||||
expect(run.listEmulators).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user