Compare commits

...

61 Commits

Author SHA1 Message Date
Erisu
d828785435 release(android-v11.0.0): updated version and RELEASENOTES.md (2) 2022-07-04 22:37:49 +09:00
エリス
e5b7e8ab26 fix(prepare): destFile path separator (#1455) 2022-07-04 22:27:11 +09:00
Erisu
f38e8eb3d0 chore: bump version 11.0.1-dev 2022-06-30 21:53:33 +09:00
Erisu
98895f7d78 release(android-v11.0.0): updated version and RELEASENOTES.md 2022-06-30 21:43:31 +09:00
エリス
e968cac0b9 fix: support installing platfrom from local git checkout (#1453) 2022-06-30 21:35:27 +09:00
エリス
861fec2cc7 dep: bump jasmine@4.2.1 w/ package-lock rebuild (#1452) 2022-06-30 20:25:06 +09:00
エリス
273d1bdecd chore: display warning on deprecated <splash> tag usage (#1451)
* chore: display warning on deprecated <splash> tag usage
* test: create spy on warnForDeprecatedSplashScreen
2022-06-30 20:00:25 +09:00
エリス
606e9c4826 feat!: android 12 splash screen (#1441)
* chore!: remove old splashscreen logic
* feat(splashscreen): add backwards compatibility
* chore: remove unused method
* chore: prefix splashscreen_background with cdv_
* feat: support android 12 splashscreen api configs
* feat: improve & refactor the logic for android splashscreen api 12
* feat: splashscreen copy image resources
* feat: splashscreen branding image & xml cleanup
* fix: splashscreen cleanup & branding conditions
* fix: splashscreen @color usage
* feat: update default Apache Cordova splash screen
* chore: add missing asf header
* fix: splashscreen image size
* chore: use Theme.SplashScreen.IconBackground as default parent to support windowSplashScreenIconBackgroundColor
* fix: center default test image by correct pivot
* fix: fs-extra copySync
* feat: re-add AutoHideSplashScreen and SplashScreenDelay preference support
* chore: move splashscreen into CordovaActivity
* feat: support splashscreen.hide & centralize to SplashScreenPlugin
* chore: cleanup SplashScreenPlugin
* feat: support fade, default auto hide on onPageFinished, support delays, refactor
* refactor: cleanup splash screen
* refactor: cleanup remove unused import
* chore: add show method as unsupported
* test: create a spy on updateProjectSplashScreen
* style: add ending new line
* chore: improve logging to warn when image path is missing
* chore: split windowSplashScreenAnimatedIcon and windowSplashScreenBrandingImage case and added branding warning
* chore: improve when to display warning
* fix: add splashscreen dependency to app as well
* chore: move the core-splashscreen dep lower

Co-authored-by: Niklas Merz <niklasmerz@linux.com>
2022-06-30 10:49:10 +09:00
jcesarmobile
2d2ad4cb81 fix: accept file cookies only if AndroidInsecureFileModeEnabled (#1449) 2022-06-29 11:36:55 +09:00
jcesarmobile
26b21219f7 feat: Update androidx.appcompat version (#1448) 2022-06-29 11:34:08 +09:00
jcesarmobile
8d6e41fd77 feat: Update gradle plugin version (#1446) 2022-06-28 12:23:42 +09:00
jcesarmobile
262a314c72 feat: Update google services pluging (#1447) 2022-06-28 12:23:10 +09:00
エリス
bf9e4d8aab fix!: set & use ANDROID_HOME as default (#1444)
* fix: remove ANDROID_HOME's DEPRECATED text
* fix: check_gradle to check ANDROID_HOME first ANDROID_SDK_ROOT last
* fix: set ANDROID_HOME
* chore: deprecate flag on ANDROID_SDK_ROOT
2022-06-27 22:07:32 +09:00
エリス
4916e1db51 fix: force hostname to lowercase (#1443) 2022-06-20 23:25:57 +09:00
エリス
68a9a3181a ci: update github action workflow (#1439)
* ci: bump actions/checkout dependency
* ci: bump actions/setup-node dependency
* ci: bump codecov/codecov-action dependency
* ci: migrate from adopt to temurin
2022-06-03 15:05:38 +09:00
jcesarmobile
ba032df665 chore: Remove unneeded deprecated annotation (#1430) 2022-06-02 01:13:25 +02:00
エリス
53d39fb135 fix: restore checkReqs in prepare.js (#1434) 2022-05-19 00:49:52 +09:00
エリス
4744bfe6bf feat: support custom compileSdk setting (#1431)
* feat: support custom compileSdk setting
* chore: apply suggestions from code review
* chore: apply cdv-gradle-config-defaults.json suggestion
* fix: set compile sdk when null
* fix: move compileSdk null check to gradle
* fix: compile sdk requirement warning & display in gradle per subproject

Co-authored-by: Norman Breau <norman@nbsolutions.ca>
2022-05-18 23:18:33 +09:00
wedgberto
cb494ff9b1 fix(android): move MainActivity.java to folder that tracks the app package name (widget id) (#1154)
* move mainactivity.java

* unit test for moving mainactivity.java

* fix new unit test

* eslint fixes

* add explicit elementtree npm dependency

Co-authored-by: David Wedgbury <david.wedgbury@telent.com>
2022-05-18 11:11:31 -03:00
ebhsgit
bd0c8ce639 [Android] Added support for BoM imports (#1311)
* Added support for BoM imports

https://docs.gradle.org/current/userguide/platforms.html#sub:bom_import

Changes

* propertiesObj.systemLibs regex - exclude the value contains (
* added propertiesObj.bomPlatforms - any value which matches platform("...")

* Fix eslint style issues

Co-authored-by: 8bhsolutions <48874658+8bhsolutions@users.noreply.github.com>
2022-05-18 01:10:15 -03:00
Norman Breau
e73000023b feat: API 32 support (#1427) 2022-05-03 20:13:17 -03:00
Norman Breau
087e9e6178 ci: Added Node 18 to test matrix (#1424) 2022-04-22 11:58:44 -03:00
エリス
a2bb7f1173 chore(npm): bump @cordova/eslint-config@^4.0.0 (#1421)
* chore(npm): bump @cordova/eslint-config@^4.0.0
* style(lint): apply auto corrections
* style(lint): convert hasAndroidHome var to let and hoisted
2022-04-18 10:39:54 +09:00
エリス
62ed71c539 chore(npm): bump dependencies (#1420)
* chore(npm): bump dev dependencies
* chore(npm): bump release dependencies
2022-04-17 22:02:40 +09:00
Norman Breau
5704ef9ea5 feat!: API 31 support (#1410)
* feat(breaking): API 31 support
* ci: bump actions/setup-java@3 w/ java 11
* ci: set setup-java distribution to adopt

Co-authored-by: Erisu <erisu@apache.org>
2022-04-17 19:43:45 +09:00
Norman Breau
adcd9d9ff8 chore(breaking): Drop Node 12 support (#1411) 2022-04-17 19:43:11 +09:00
エリス
f3c75a89b5 chore: bump for next major 11.0.0-dev (#1419) 2022-04-17 19:06:51 +09:00
Erisu
21e7c2f661 chore: bump version 10.1.3-dev 2022-04-11 22:10:31 +09:00
Erisu
f12080b7e2 android-v10.1.2 Updated version and RELEASENOTES.md for release 10.1.2 2022-04-11 22:01:02 +09:00
Norman Breau
51291f8985 chore: update package-lock to satisfy npm audit (#1413) 2022-04-06 11:59:21 -03:00
Lars Mikkelsen
112f0a61a8 fix: detect JAVA_HOME with Java 11 (#1406) 2022-03-17 20:38:04 -03:00
Norman Breau
6d3ce211dd fix(GH-1391): Reword minimum build tools version to make it more clear what is actually required. (#1401) 2022-02-22 10:14:22 -04:00
Tiago Pereira
f100809bf3 fix: escape strings.xml app name (#1384) 2022-02-08 11:29:36 +09:00
エリス
a1ed1c0af7 feat(AndroidManifest): explicitly define the activity attribute android:exported (#1372) 2021-10-28 17:29:55 +09:00
Norman Breau
05e3e3cf8d chore: npmrc (#1348) 2021-09-17 09:54:59 -03:00
Erisu
8a957fb9c9 chore: bump version 10.1.2-dev 2021-09-13 20:17:31 +09:00
Erisu
e188c61c86 android-v10.1.1 Updated version and RELEASENOTES.md for release 10.1.1 2021-09-13 15:56:27 +09:00
エリス
ca19084b1c fix(PluginManager): AllowNavigation default policy to handle scheme & hostname (#1349) 2021-09-13 15:48:02 +09:00
Joel Skrepnek
aea6b7f6f4 fix(AllowListPlugin): Safely handle default allow navigation policy in allow request (#1342) 2021-09-09 14:58:58 +09:00
エリス
7a67e00b9f fix(PluginManager): AllowBridgeAccess default policy to handle scheme & hostname (#1332) 2021-09-07 19:52:23 +09:00
Erisu
dc4e065f61 chore: bump version 10.1.1-dev 2021-08-13 13:42:59 +09:00
Erisu
c3fd6bca4a android-v10.1.0 Updated version and RELEASENOTES.md for release 10.1.0 2021-08-13 13:26:31 +09:00
Alexis THOMAS
13bd3f4a9f feat: unify create default values & stop project name transform (#1213) 2021-08-13 12:52:30 +09:00
Alexis THOMAS
09c75237d9 fix: display project name in Android Studio (#1214) 2021-08-13 12:08:18 +09:00
Alexis THOMAS
b5e79b5a4d doc: update README about development & testing (#1308)
Co-authored-by: エリス <erisu@users.noreply.github.com>
2021-08-13 11:28:47 +09:00
Mosab A
5db850890d feat: bump ANDROIDX_APP_COMPAT@1.3.1 (#1306) 2021-08-02 16:34:06 +09:00
Mosab A
b6c5db3e37 feat: bump Google Services Gradle Plugin@4.3.8 (#1303) 2021-08-02 14:21:11 +09:00
Mosab A
cba3410b17 feat: bump kotlin@1.5.21 (#1302) 2021-08-02 14:21:02 +09:00
エリス
565ac9c7b1 fix: fall back to project root repositories.gradle (#1300) 2021-08-02 14:19:40 +09:00
エリス
1636d70f25 feat: support http w/ content src fix (#1298) 2021-07-30 17:08:23 +09:00
エリス
4e5892c5ed chore: bump version 10.1.0-dev (#1301) 2021-07-30 17:08:04 +09:00
Erisu
e69ab6a687 chore: bump version 10.0.2-dev 2021-07-27 23:23:36 +09:00
Erisu
a9171c3db6 android-v10.0.1 Updated version and RELEASENOTES.md for release 10.0.1 2021-07-27 22:50:34 +09:00
エリス
90271301fb fix: maven-publish setup (#1295) 2021-07-27 21:03:14 +09:00
Norman Breau
c11fcc10e8 fix: gradle build tools config (#1293) 2021-07-26 13:26:42 -03:00
Norman Breau
5e50c1d611 fix: automatic latest build tools finding (#1294) 2021-07-26 13:17:25 -03:00
エリス
38ca895459 fix: add missing release notes (#1291)
* CB-14203 Update RELEASENOTES & version for 7.1.1
* Update RELEASENOTES & version for v7.1.2
* Update VERSION & RELEASENOTES for 7.1.3
* Update VERSION & RELEASENOTES for 7.1.4

Co-authored-by: Christopher J. Brody <chris.brody@gmail.com>
Co-authored-by: Darryl Pogue <darryl@dpogue.ca>
2021-07-24 21:44:17 +09:00
エリス
0ce66249da fix: Google Services Gradle Plugin version check failure (#1287) 2021-07-20 17:49:21 +09:00
エリス
291edcd24e chore: update readme requirements (#1286) 2021-07-20 17:49:13 +09:00
エリス
4f44036d0a chore: set version for patch release (#1285) 2021-07-20 17:49:04 +09:00
Erisu
6081cc7442 Set VERSION to 10.1.0-dev (via coho) 2021-07-17 16:24:18 +09:00
69 changed files with 7261 additions and 2178 deletions

View File

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

2
.npmrc Normal file
View File

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

View File

@@ -28,22 +28,44 @@
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).
[Apache Cordova](https://cordova.apache.org/) is a project of [The Apache Software Foundation (ASF)](https://apache.org/).
## Requires
## Requirements
- Java JDK 1.8
- Android SDK [http://developer.android.com](https://developer.android.com/)
* Java Development Kit (JDK) 11
* [Android SDK](https://developer.android.com/)
* [Node.js](https://nodejs.org)
## Cordova Android Developer Tools
## Create a Cordova project
Use the [Cordova command-line tool](https://www.npmjs.com/package/cordova) to create projects and install plugins.
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/)
## Using Android Studio
To use a **shared framework**, for example in development, link the appropriate cordova-android platform folder path:
1. Create a project
2. Import it via "Non-Android Studio Project"
```bash
cordova platform add --link /path/to/cordova-android
```
## Running the Native Tests
## Updating a Cordova project
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!
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/)

View File

@@ -20,6 +20,98 @@
-->
## 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:**
* [GH-1295](https://github.com/apache/cordova-android/pull/1295) fix: `maven-publish` setup
* [GH-1293](https://github.com/apache/cordova-android/pull/1293) fix: `gradle` build tools config
* [GH-1294](https://github.com/apache/cordova-android/pull/1294) fix: automatic latest build tools finding
* [GH-1287](https://github.com/apache/cordova-android/pull/1287) fix: Google Services Gradle Plugin version check failure
**Chores:**
* [GH-1291](https://github.com/apache/cordova-android/pull/1291) chore: add missing release notes
* [GH-1286](https://github.com/apache/cordova-android/pull/1286) chore: update `README` requirements
### 10.0.0 (Jul 17, 2021)
**Breaking:**
@@ -312,6 +404,38 @@
* [CB-13830](https://issues.apache.org/jira/browse/CB-13830) Add handlers for plugins that use non-Java source files, such as Camera
* [CB-13923](https://issues.apache.org/jira/browse/CB-13923) Fix -1 length for compressed files
### 7.1.4 (Nov 22, 2018)
* Update android-versions to `1.4.0`, with added support for Android Pie ([#573](https://github.com/apache/cordova-android/pull/573))
* Output current package name if package name can't be validated ([#567](https://github.com/apache/cordova-android/pull/567))
* Resolve issue with plugin `target-dir="*app*"` subdirs ([#572](https://github.com/apache/cordova-android/pull/572))
### 7.1.3 (Nov 19, 2018)
* [GH-495](https://github.com/apache/cordova-android/pull/495) Incorrect default sdk version issue fix
* [GH-496](https://github.com/apache/cordova-android/pull/496) update comments in `build.gradle`
* [GH-539](https://github.com/apache/cordova-android/pull/539) Fix dest overwrite, in case of of plugin `source-file` element with `target-dir` that does not need remapping
* [GH-540](https://github.com/apache/cordova-android/issues/540) support plugin `source-file` element with any app `target-dir` value
* [GH-547](https://github.com/apache/cordova-android/issues/547) Compatibility of old plugins with non-Java `source-file` entries (individual files)
* [GH-551](https://github.com/apache/cordova-android/pull/551) add missing cast for cdvMinSdkVersion to `build.gradle`
* [GH-552](https://github.com/apache/cordova-android/issues/552) check for `build-extras.gradle` in the parent app directory
### 7.1.2 (Nov 08, 2018)
* [CB-14127](https://issues.apache.org/jira/browse/CB-14127): Always put the Google repo above jcenter
* [CB-14165](https://issues.apache.org/jira/browse/CB-14165): Emulator: handle "device still connecting" error (#457)
* [CB-14125](https://issues.apache.org/jira/browse/CB-14125): Increase old plugin compatibility
* [CB-13830](https://issues.apache.org/jira/browse/CB-13830): Add handlers for plugins that use non-Java source files, such as Camera
* [CB-14038](https://issues.apache.org/jira/browse/CB-14038): fix false positive detecting project type
### 7.1.1 (Jul 11, 2018)
* Fix unsafe property access in run.js (#445)
* Emit log event instead of logging directly (#452)
* [CB-14101](https://issues.apache.org/jira/browse/CB-14101) Fix Java version check for Java >= 9 (#446)
* [CB-14127](https://issues.apache.org/jira/browse/CB-14127) (android) Move google maven repo ahead of jcenter
* [CB-13923](https://issues.apache.org/jira/browse/CB-13923) (android) fix -1 length for compressed files
* [CB-14145](https://issues.apache.org/jira/browse/CB-14145) use cordova-common@2.2.5 and update other dependencies to resolve `npm audit` warnings
* [CB-9366](https://issues.apache.org/jira/browse/CB-9366) log error.stack in cordova.js
### 7.1.0 (Feb 20, 2018)
* [CB-13879](https://issues.apache.org/jira/browse/CB-13879) updated gradle tools dependency to 3.0.1 for project template
* [CB-13831](https://issues.apache.org/jira/browse/CB-13831) Update `android-versions` to 1.3.0 to support SDK 27.

View File

@@ -36,6 +36,9 @@ 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

@@ -0,0 +1,33 @@
/*
*
* 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

@@ -26,17 +26,25 @@ buildscript {
// Android Gradle Plugin (AGP) Build Tools
classpath "com.android.tools.build:gradle:${cordovaConfig.AGP_VERSION}"
}
cdvHelpers.verifyCordovaConfigForBuild()
}
allprojects {
apply from: 'repositories.gradle'
def hasRepositoriesGradle = file('repositories.gradle').exists()
if (hasRepositoriesGradle) {
apply from: 'repositories.gradle'
} else {
apply from: "${project.rootDir}/repositories.gradle"
}
repositories repos
}
apply plugin: 'com.android.library'
android {
compileSdkVersion cordovaConfig.SDK_VERSION
compileSdkVersion cordovaConfig.COMPILE_SDK_VERSION
buildToolsVersion cordovaConfig.BUILD_TOOLS_VERSION
compileOptions {
@@ -72,6 +80,7 @@ android {
dependencies {
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,13 +1,15 @@
{
"MIN_SDK_VERSION": 22,
"SDK_VERSION": 30,
"GRADLE_VERSION": "7.1.1",
"BUILD_TOOLS_VERSION": "30.0.3",
"AGP_VERSION": "4.2.2",
"KOTLIN_VERSION": "1.5.20",
"ANDROIDX_APP_COMPAT_VERSION": "1.3.0",
"SDK_VERSION": 32,
"COMPILE_SDK_VERSION": null,
"GRADLE_VERSION": "7.4.2",
"MIN_BUILD_TOOLS_VERSION": "32.0.0",
"AGP_VERSION": "7.2.1",
"KOTLIN_VERSION": "1.5.21",
"ANDROIDX_APP_COMPAT_VERSION": "1.4.2",
"ANDROIDX_WEBKIT_VERSION": "1.4.0",
"GRADLE_PLUGIN_GOOGLE_SERVICES_VERSION": "4.3.5",
"ANDROIDX_CORE_SPLASHSCREEN_VERSION": "1.0.0-rc01",
"GRADLE_PLUGIN_GOOGLE_SERVICES_VERSION": "4.3.10",
"IS_GRADLE_PLUGIN_GOOGLE_SERVICES_ENABLED": false,
"IS_GRADLE_PLUGIN_KOTLIN_ENABLED": false
}

View File

@@ -18,6 +18,7 @@
*/
apply plugin: 'maven-publish'
apply plugin: 'signing'
String getCordovaAndroidVersion() {
// Fetch Data from Cordova-Android package.json (Used only by framework build/publishing)
@@ -29,6 +30,22 @@ String getCordovaAndroidVersion() {
}
}
// Enable signing by default when keyId and secretKeyRingFile is defined.
ext.cdvEnableSigning = project.hasProperty('signing.keyId') && project.hasProperty('signing.secretKeyRingFile')
if (cdvEnableSigning) {
logger.debug('[Cordova] Signing has been enabled by default because the signing keyId & secretKeyRingFile has been defined.')
}
if (project.hasProperty('signEnabled')) {
if(!cdvEnableSigning && Boolean.valueOf(signEnabled)) {
logger.debug("[Cordova] The \"signEnabled\" override can not be set to \"true\" when the signing properties are missing.")
} else {
// Override the default setting with the "signEnabled" property. (In this case it should only accept false)
logger.debug("[Cordova] The \"signEnabled\" property has been detected and forcing enabled signing to \"$signEnabled\".")
cdvEnableSigning = signEnabled
}
}
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
@@ -36,7 +53,7 @@ task sourcesJar(type: Jar) {
publishing {
publications {
Cordova(MavenPublication) {
mavenJava(MavenPublication) {
groupId = 'org.apache.cordova'
artifactId = 'framework'
version = getCordovaAndroidVersion()
@@ -76,4 +93,33 @@ publishing {
}
}
}
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
credentials {
if (project.hasProperty('apacheUsername') && project.hasProperty('apachePassword')) {
username apacheUsername
password apachePassword
}
}
}
}
signing {
if (Boolean.valueOf(cdvEnableSigning)) {
sign publishing.publications.mavenJava
}
}
}
tasks.whenTaskAdded {task ->
if(task.name.contains('sign')) {
logger.debug("[Cordova] The task \"${task.name}\" is enabled? ${cdvEnableSigning}")
task.enabled = cdvEnableSigning
}
}

View File

@@ -53,24 +53,27 @@ String doFindLatestInstalledBuildTools(String minBuildToolsVersionString) {
throw e
}
def minBuildToolsVersion = new Version(minBuildToolsVersionString)
def maxVersion = new Version((minBuildToolsVersion.getMajor() + 1) + ".0.0")
def highestBuildToolsVersion = buildToolsDirContents
.collect { new Version(it) }
// Invalid inputs will be handled as 0.0.0
.findAll { it.isHigherThan('0.0.0') }
.findAll { it.isHigherThan('0.0.0') && it.isLowerThan(maxVersion) }
.max()
if (highestBuildToolsVersion == null) {
throw new RuntimeException("""
No installed build tools found. Install the Android build tools
version ${minBuildToolsVersionString} or higher.
No installed build tools found. Please install the Android build tools
version ${minBuildToolsVersionString}.
""".replaceAll(/\s+/, ' ').trim())
}
if (highestBuildToolsVersion.isLowerThan(minBuildToolsVersionString)) {
throw new RuntimeException("""
No usable Android build tools found. Highest installed version is
${highestBuildToolsVersion.getOriginalString()}; minimum version
required is ${minBuildToolsVersionString}.
No usable Android build tools found. Highest ${minBuildToolsVersion.getMajor()}.x installed version is
${highestBuildToolsVersion.getOriginalString()}; Recommended version
is ${minBuildToolsVersionString}.
""".replaceAll(/\s+/, ' ').trim())
}
@@ -158,6 +161,9 @@ 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)
}
@@ -171,10 +177,26 @@ def doApplyCordovaConfigCustomization() {
cordovaConfig.ANDROIDX_WEBKIT_VERSION = cdvAndroidXWebKitVersion
}
// Ensure the latest installed build tools is selected, with or without defined override
cordovaConfig.LATEST_INSTALLED_BUILD_TOOLS = doFindLatestInstalledBuildTools(
cordovaConfig.BUILD_TOOLS_VERSION
)
if (!cordovaConfig.BUILD_TOOLS_VERSION) {
cordovaConfig.BUILD_TOOLS_VERSION = doFindLatestInstalledBuildTools(
cordovaConfig.MIN_BUILD_TOOLS_VERSION
)
}
// Ensure the configured build tools version is at least our declared minimum
def buildToolsVersion = new Version(cordovaConfig.BUILD_TOOLS_VERSION)
if (buildToolsVersion.isLowerThan(cordovaConfig.MIN_BUILD_TOOLS_VERSION)) {
throw new RuntimeException("""
Expected Android Build Tools version >= ${cordovaConfig.MIN_BUILD_TOOLS_VERSION},
but got Android Build Tools version ${cordovaConfig.BUILD_TOOLS_VERSION}. Please use version ${cordovaConfig.MIN_BUILD_TOOLS_VERSION} or later.
""".replaceAll(/\s+/, ' ').trim())
}
}
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.
@@ -197,6 +219,10 @@ 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()
@@ -214,6 +240,8 @@ 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,11 +82,6 @@ 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)) {
@@ -127,7 +122,7 @@ public class AllowListPlugin extends CordovaPlugin {
@Override
public Boolean shouldAllowRequest(String url) {
return (this.shouldAllowNavigation(url) || this.allowedRequests.isUrlAllowListed(url))
return (Boolean.TRUE.equals(this.shouldAllowNavigation(url)) || this.allowedRequests.isUrlAllowListed(url))
? true
: null; // default policy
}

View File

@@ -24,16 +24,19 @@ import java.util.ArrayList;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
public class ConfigXmlParser {
private static String TAG = "ConfigXmlParser";
private String launchUrl = null;
private static String SCHEME_HTTP = "http";
private static String SCHEME_HTTPS = "https";
private static String DEFAULT_HOSTNAME = "localhost";
private String launchUrl;
private String contentSrc;
private CordovaPreferences prefs = new CordovaPreferences();
private ArrayList<PluginEntry> pluginEntries = new ArrayList<PluginEntry>(20);
@@ -47,11 +50,7 @@ public class ConfigXmlParser {
public String getLaunchUrl() {
if (launchUrl == null) {
launchUrl = "https://" + this.prefs.getString("hostname", "localhost");
}
if (this.prefs.getBoolean("AndroidInsecureFileModeEnabled", false)) {
launchUrl = "file:///android_asset/www/index.html";
setStartUrl(contentSrc);
}
return launchUrl;
@@ -77,6 +76,14 @@ public class ConfigXmlParser {
)
);
pluginEntries.add(
new PluginEntry(
SplashScreenPlugin.PLUGIN_NAME,
"org.apache.cordova.SplashScreenPlugin",
true
)
);
parse(action.getResources().getXml(id));
}
@@ -130,7 +137,10 @@ public class ConfigXmlParser {
else if (strNode.equals("content")) {
String src = xml.getAttributeValue(null, "src");
if (src != null) {
setStartUrl(src);
contentSrc = src;
} else {
// Default
contentSrc = "index.html";
}
}
}
@@ -147,20 +157,40 @@ public class ConfigXmlParser {
}
}
private String getLaunchUrlPrefix() {
if (prefs.getBoolean("AndroidInsecureFileModeEnabled", false)) {
return "file:///android_asset/www/";
} else {
String scheme = prefs.getString("scheme", SCHEME_HTTPS).toLowerCase();
String hostname = prefs.getString("hostname", DEFAULT_HOSTNAME).toLowerCase();
if (!scheme.contentEquals(SCHEME_HTTP) && !scheme.contentEquals(SCHEME_HTTPS)) {
LOG.d(TAG, "The provided scheme \"" + scheme + "\" is not valid. " +
"Defaulting to \"" + SCHEME_HTTPS + "\". " +
"(Valid Options=" + SCHEME_HTTP + "," + SCHEME_HTTPS + ")");
scheme = SCHEME_HTTPS;
}
return scheme + "://" + hostname + '/';
}
}
private void setStartUrl(String src) {
Pattern schemeRegex = Pattern.compile("^[a-z-]+://");
Matcher matcher = schemeRegex.matcher(src);
if (matcher.find()) {
launchUrl = src;
} else {
String launchUrlPrefix = getLaunchUrlPrefix();
// remove leading slash, "/", from content src if existing,
if (src.charAt(0) == '/') {
src = src.substring(1);
}
if (this.prefs.getBoolean("AndroidInsecureFileModeEnabled", false)) {
launchUrl = "file:///android_asset/www/" + src;
} else {
launchUrl = "https://" + this.prefs.getString("hostname", "localhost") + "/" + src;
}
launchUrl = launchUrlPrefix + src;
}
}
}

View File

@@ -42,6 +42,7 @@ 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
@@ -98,11 +99,16 @@ 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();
@@ -125,8 +131,6 @@ 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,
@@ -153,6 +157,9 @@ 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))) {
@@ -526,5 +533,4 @@ public class CordovaActivity extends AppCompatActivity {
}
}
}

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 = "10.0.0-dev";
public static final String CORDOVA_VERSION = "11.0.0";
void init(CordovaInterface cordova, List<PluginEntry> pluginEntries, CordovaPreferences preferences);

View File

@@ -41,6 +41,12 @@ 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
@@ -366,6 +372,24 @@ 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.
*
@@ -431,7 +455,7 @@ public class PluginManager {
}
// Default policy:
return url.startsWith("file://") || url.startsWith("about:blank");
return url.startsWith(getLaunchUrlPrefix()) || url.startsWith("about:blank");
}
@@ -452,7 +476,7 @@ public class PluginManager {
}
// Default policy:
return url.startsWith("file://");
return url.startsWith(getLaunchUrlPrefix());
}
/**

View File

@@ -0,0 +1,168 @@
/*
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();
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.
// 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(isFadeEnabled ? fadeDuration : 0)
.setStartDelay(isFadeEnabled ? 0 : fadeDuration)
.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,8 +19,6 @@
package org.apache.cordova.engine;
import android.annotation.TargetApi;
import android.os.Build;
import android.webkit.CookieManager;
import android.webkit.WebView;
@@ -35,10 +33,14 @@ class SystemCookieManager implements ICordovaCookieManager {
webView = webview;
cookieManager = CookieManager.getInstance();
cookieManager.setAcceptFileSchemeCookies(true);
cookieManager.setAcceptThirdPartyCookies(webView, true);
}
@SuppressWarnings("deprecation")
public void setAcceptFileSchemeCookies() {
cookieManager.setAcceptFileSchemeCookies(true);
}
public void setCookiesEnabled(boolean accept) {
cookieManager.setAcceptCookie(accept);
}
@@ -51,7 +53,6 @@ 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"))
.setDomain(parentEngine.preferences.getString("hostname", "localhost").toLowerCase())
.setHttpAllowed(true);
assetLoaderBuilder.addPathHandler("/", path -> {

View File

@@ -165,6 +165,7 @@ public class SystemWebViewEngine implements CordovaWebViewEngine {
LOG.d(TAG, "Enabled insecure file access");
settings.setAllowFileAccess(true);
settings.setAllowUniversalAccessFromFileURLs(true);
cookieManager.setAcceptFileSchemeCookies();
}
settings.setMediaPlaybackRequiresUserGesture(false);

View File

@@ -17,12 +17,12 @@
under the License.
*/
var os = require('os');
var execa = require('execa');
var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;
const os = require('os');
const execa = require('execa');
const events = require('cordova-common').events;
const CordovaError = require('cordova-common').CordovaError;
var Adb = {};
const 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 + '...');
var args = ['-s', target, 'install'];
const 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 + '...');
var args = ['-s', target, 'shell'];
const 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.
*/
var fs = require('fs');
var xml = require('cordova-common').xmlHelpers;
const fs = require('fs');
const xml = require('cordova-common').xmlHelpers;
var DEFAULT_ORIENTATION = 'default';
const DEFAULT_ORIENTATION = 'default';
/** Wraps an AndroidManifest file */
class AndroidManifest {
@@ -60,7 +60,7 @@ class AndroidManifest {
}
getActivity () {
var activity = this.doc.getroot().find('./application/activity');
const activity = this.doc.getroot().find('./application/activity');
return {
getName: function () {
return activity.attrib['android:name'];
@@ -103,7 +103,7 @@ class AndroidManifest {
}
setDebuggable (value) {
var application = this.doc.getroot().find('./application');
const application = this.doc.getroot().find('./application');
if (value) {
application.attrib['android:debuggable'] = 'true';
} else {

View File

@@ -17,16 +17,16 @@
under the License.
*/
var fs = require('fs');
var path = require('path');
var properties_parser = require('properties-parser');
var AndroidManifest = require('./AndroidManifest');
var pluginHandlers = require('./pluginHandlers');
const fs = require('fs');
const path = require('path');
const properties_parser = require('properties-parser');
const AndroidManifest = require('./AndroidManifest');
const pluginHandlers = require('./pluginHandlers');
var projectFileCache = {};
let projectFileCache = {};
function addToPropertyList (projectProperties, key, value) {
var i = 1;
let 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) {
var i = 1;
var currentValue;
let i = 1;
let 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) {
var libraryPath = path.relative(parentDir, subDir);
const libraryPath = path.relative(parentDir, subDir);
return (path.sep === '\\') ? libraryPath.replace(/\\/g, '/') : libraryPath;
}
@@ -72,27 +72,27 @@ class AndroidProject {
* @return {String} The name of the package
*/
getPackageName () {
var manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml');
const 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.
var packageName = this.getPackageName();
var lastDotIndex = packageName.lastIndexOf('.');
var prefix = packageName.substring(lastDotIndex + 1);
var subRelativeDir = path.join(plugin_id, prefix + '-' + path.basename(src));
const packageName = this.getPackageName();
const lastDotIndex = packageName.lastIndexOf('.');
const prefix = packageName.substring(lastDotIndex + 1);
const subRelativeDir = path.join(plugin_id, prefix + '-' + path.basename(src));
return subRelativeDir;
}
addSubProject (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var subProjectFile = path.resolve(subDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
const parentProjectFile = path.resolve(parentDir, 'project.properties');
const subProjectFile = path.resolve(subDir, 'project.properties');
const parentProperties = this._getPropertiesFile(parentProjectFile);
// TODO: Setting the target needs to happen only for pre-3.7.0 projects
if (fs.existsSync(subProjectFile)) {
var subProperties = this._getPropertiesFile(subProjectFile);
const 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) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
const parentProjectFile = path.resolve(parentDir, 'project.properties');
const parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir));
delete this._subProjectDirs[subDir];
this._dirty = true;
}
addGradleReference (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
const parentProjectFile = path.resolve(parentDir, 'project.properties');
const parentProperties = this._getPropertiesFile(parentProjectFile);
addToPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
}
removeGradleReference (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
const parentProjectFile = path.resolve(parentDir, 'project.properties');
const parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
}
addSystemLibrary (parentDir, value) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
const parentProjectFile = path.resolve(parentDir, 'project.properties');
const parentProperties = this._getPropertiesFile(parentProjectFile);
addToPropertyList(parentProperties, 'cordova.system.library', value);
this._dirty = true;
}
removeSystemLibrary (parentDir, value) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
const parentProjectFile = path.resolve(parentDir, 'project.properties');
const parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'cordova.system.library', value);
this._dirty = true;
}
@@ -144,8 +144,8 @@ class AndroidProject {
}
this._dirty = false;
for (var filename in this._propertiesEditors) {
var editor = this._propertiesEditors[filename];
for (const filename in this._propertiesEditors) {
const 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 () {
var build_path = path.join(this.projectDir, 'build');
const 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.
*/
var path = require('path');
const path = require('path');
var AndroidProject = require('./AndroidProject');
var PluginManager = require('cordova-common').PluginManager;
const AndroidProject = require('./AndroidProject');
const PluginManager = require('cordova-common').PluginManager;
var CordovaLogger = require('cordova-common').CordovaLogger;
var selfEvents = require('cordova-common').events;
var ConfigParser = require('cordova-common').ConfigParser;
const CordovaLogger = require('cordova-common').CordovaLogger;
const selfEvents = require('cordova-common').events;
const ConfigParser = require('cordova-common').ConfigParser;
const prepare = require('./prepare').prepare;
var PLATFORM = 'android';
const PLATFORM = 'android';
const VERSION = require('../package').version;
function setupEvents (externalEventEmitter) {
@@ -73,6 +73,8 @@ 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')
@@ -88,7 +90,7 @@ class Api {
* platform's file structure and other properties of platform.
*/
getPlatformInfo () {
var result = {};
const result = {};
result.locations = this.locations;
result.root = this.root;
result.name = this.platform;
@@ -139,8 +141,8 @@ class Api {
* CordovaError instance.
*/
addPlugin (plugin, installOptions) {
var project = AndroidProject.getProjectFile(this.root);
var self = this;
const project = AndroidProject.getProjectFile(this.root);
const self = this;
installOptions = installOptions || {};
installOptions.variables = installOptions.variables || {};
@@ -175,7 +177,7 @@ class Api {
* CordovaError instance.
*/
removePlugin (plugin, uninstallOptions) {
var project = AndroidProject.getProjectFile(this.root);
const project = AndroidProject.getProjectFile(this.root);
if (uninstallOptions && uninstallOptions.usePlatformWww === true) {
uninstallOptions.usePlatformWww = false;
@@ -239,7 +241,7 @@ class Api {
* arhcitectures is specified.
*/
build (buildOptions) {
var self = this;
const self = this;
return require('./check_reqs').run().then(function () {
return require('./build').run.call(self, buildOptions);
@@ -269,7 +271,7 @@ class Api {
* successfully, or rejected with CordovaError.
*/
run (runOptions) {
var self = this;
const self = this;
return require('./check_reqs').run().then(function () {
return require('./run').run.call(self, runOptions);
});
@@ -283,7 +285,7 @@ class Api {
* CordovaError.
*/
clean (cleanOptions) {
var self = this;
const self = this;
// This will lint, checking for null won't
if (typeof cleanOptions === 'undefined') {
cleanOptions = {};
@@ -328,7 +330,7 @@ class Api {
*/
static createPlatform (destination, config, options, events) {
events = setupEvents(events);
var result;
let result;
try {
result = require('./create').create(destination, config, options, events).then(function (destination) {
return new Api(PLATFORM, destination, events);
@@ -358,7 +360,7 @@ class Api {
*/
static updatePlatform (destination, options, events) {
events = setupEvents(events);
var result;
let 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');
var suffix_number_regex = /(\d+)$/;
const 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) {
var target_out = output.split('\n');
var targets = [];
for (var i = target_out.length - 1; i >= 0; i--) {
const target_out = output.split('\n');
const targets = [];
for (let 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.
*/
var path = require('path');
var fs = require('fs');
var nopt = require('nopt');
const path = require('path');
const fs = require('fs');
const nopt = require('nopt');
const untildify = require('untildify');
var Adb = require('./Adb');
const Adb = require('./Adb');
var events = require('cordova-common').events;
var PackageType = require('./PackageType');
const events = require('cordova-common').events;
const 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
var ret = {
const 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);
}
var packageArgs = {};
const 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]; }
});
var buildConfig = options.buildConfig;
const 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));
var buildjson = fs.readFileSync(buildConfig, 'utf8');
var config = JSON.parse(buildjson.replace(/^\ufeff/, '')); // Remove BOM
const buildjson = fs.readFileSync(buildConfig, 'utf8');
const config = JSON.parse(buildjson.replace(/^\ufeff/, '')); // Remove BOM
if (config.android && config.android[ret.buildType]) {
var androidInfo = config.android[ret.buildType];
const 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) {
var opts = parseOpts(options, null, this.root);
var builder = this._builder;
const opts = parseOpts(options, null, this.root);
const 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) {
var opts = parseOpts(options, optResolvedTarget, this.root);
var builder = this._builder;
const opts = parseOpts(options, optResolvedTarget, this.root);
const 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 () {
var paths;
let 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'));
@@ -202,17 +202,17 @@ module.exports.detectArchitecture = function (target) {
};
module.exports.findBestApkForArchitecture = function (buildResults, arch) {
var paths = buildResults.apkPaths.filter(function (p) {
var apkName = path.basename(p);
const paths = buildResults.apkPaths.filter(function (p) {
const apkName = path.basename(p);
if (buildResults.buildType === 'debug') {
return /-debug/.exec(apkName);
}
return !/-debug/.exec(apkName);
});
var archPattern = new RegExp('-' + arch);
var hasArchPattern = /-x86|-arm/;
for (var i = 0; i < paths.length; ++i) {
var apkName = path.basename(paths[i]);
const archPattern = new RegExp('-' + arch);
const hasArchPattern = /-x86|-arm/;
for (let i = 0; i < paths.length; ++i) {
const apkName = path.basename(paths[i]);
if (hasArchPattern.exec(apkName)) {
if (archPattern.exec(apkName)) {
return paths[i];

View File

@@ -17,14 +17,14 @@
under the License.
*/
var fs = require('fs-extra');
var path = require('path');
const fs = require('fs-extra');
const path = require('path');
const execa = require('execa');
const glob = require('fast-glob');
var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;
var check_reqs = require('../check_reqs');
var PackageType = require('../PackageType');
const events = require('cordova-common').events;
const CordovaError = require('cordova-common').CordovaError;
const check_reqs = require('../check_reqs');
const PackageType = require('../PackageType');
const { compareByAll } = require('../utils');
const { createEditor } = require('properties-parser');
@@ -117,8 +117,8 @@ class ProjectBuilder {
* This returns a promise
*/
runGradleWrapper (gradle_cmd) {
var gradlePath = path.join(this.root, 'gradlew');
var wrapperGradle = path.join(this.root, 'wrapper.gradle');
const gradlePath = path.join(this.root, 'gradlew');
const 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 {
@@ -128,47 +128,48 @@ class ProjectBuilder {
readProjectProperties () {
function findAllUniq (data, r) {
var s = {};
var m;
const s = {};
let m;
while ((m = r.exec(data))) {
s[m[1]] = 1;
}
return Object.keys(s);
}
var data = fs.readFileSync(path.join(this.root, 'project.properties'), 'utf8');
const 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)
systemLibs: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=((?!.*\().*)(?:\s|$)/mg),
bomPlatforms: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=platform\((?:'|")(.*)(?:'|")\)/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);
const manifestPath = path.join(this.root, 'app', 'src', 'main', 'AndroidManifest.xml');
const manifestData = fs.readFileSync(manifestPath, 'utf8');
const 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('.');
const packageName = m[1];
const 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.
var pluginBuildGradle = path.join(__dirname, 'plugin-build.gradle');
var propertiesObj = this.readProjectProperties();
var subProjects = propertiesObj.libs;
const pluginBuildGradle = path.join(__dirname, 'plugin-build.gradle');
const propertiesObj = this.readProjectProperties();
const subProjects = propertiesObj.libs;
// Check and copy the gradle file into the subproject
// Called by the loop before this function def
var checkAndCopy = function (subProject, root) {
var subProjectGradle = path.join(root, subProject, 'build.gradle');
const checkAndCopy = function (subProject, root) {
const 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 {
@@ -178,34 +179,42 @@ class ProjectBuilder {
}
};
for (var i = 0; i < subProjects.length; ++i) {
for (let i = 0; i < subProjects.length; ++i) {
if (subProjects[i] !== 'CordovaLib') {
checkAndCopy(subProjects[i], this.root);
}
}
var name = this.extractRealProjectNameFromManifest();
const projectName = this.extractRealProjectNameFromManifest();
// Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
var settingsGradlePaths = subProjects.map(function (p) {
var realDir = p.replace(/[/\\]/g, ':');
var libName = realDir.replace(name + '-', '');
var str = 'include ":' + libName + '"\n';
if (realDir.indexOf(name + '-') !== -1) {
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) {
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' +
'include ":"\n' + settingsGradlePaths.join(''));
'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'), '');
}
// Update dependencies within build.gradle.
var buildGradle = fs.readFileSync(path.join(this.root, 'app', 'build.gradle'), 'utf8');
var depsList = '';
var root = this.root;
var insertExclude = function (p) {
var gradlePath = path.join(root, p, 'build.gradle');
var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
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');
if (projectGradleFile.indexOf('CordovaLib') !== -1) {
depsList += '{\n exclude module:("CordovaLib")\n }\n';
} else {
@@ -214,26 +223,39 @@ class ProjectBuilder {
};
subProjects.forEach(function (p) {
events.emit('log', 'Subproject Path: ' + p);
var libName = p.replace(/[/\\]/g, ':').replace(name + '-', '');
const libName = p.replace(/[/\\]/g, ':').replace(projectName + '-', '');
if (libName !== 'app') {
depsList += ' implementation(project(path: ":' + libName + '"))';
insertExclude(p);
}
});
// For why we do this mapping: https://issues.apache.org/jira/browse/CB-8390
var SYSTEM_LIBRARY_MAPPINGS = [
const 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) {
var mavenRef;
let 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 (var i = 0; i < SYSTEM_LIBRARY_MAPPINGS.length; ++i) {
var pair = SYSTEM_LIBRARY_MAPPINGS[i];
for (let i = 0; i < SYSTEM_LIBRARY_MAPPINGS.length; ++i) {
const pair = SYSTEM_LIBRARY_MAPPINGS[i];
if (pair[0].exec(p)) {
mavenRef = p.replace(pair[0], pair[1]);
break;
@@ -247,7 +269,7 @@ class ProjectBuilder {
});
buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + ' $2');
var includeList = '';
let includeList = '';
propertiesObj.gradleIncludes.forEach(function (includePath) {
includeList += 'apply from: "../' + includePath + '"\n';
@@ -258,7 +280,7 @@ class ProjectBuilder {
}
prepEnv (opts) {
var self = this;
const self = this;
return check_reqs.check_gradle()
.then(function (gradlePath) {
return self.runGradleWrapper(gradlePath);
@@ -301,8 +323,8 @@ class ProjectBuilder {
* Returns a promise.
*/
async build (opts) {
var wrapper = path.join(this.root, 'gradlew');
var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
const wrapper = path.join(this.root, 'gradlew');
const 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.SDK_VERSION
compileSdkVersion cordovaConfig.COMPILE_SDK_VERSION
buildToolsVersion cordovaConfig.BUILD_TOOLS_VERSION
compileOptions {

View File

@@ -18,12 +18,12 @@
*/
const execa = require('execa');
var path = require('path');
var fs = require('fs-extra');
const path = require('path');
const fs = require('fs-extra');
const { forgivingWhichSync, isWindows, isDarwin } = require('./utils');
const java = require('./env/java');
const { CordovaError, ConfigParser, events } = require('cordova-common');
var android_sdk = require('./android_sdk');
const android_sdk = require('./android_sdk');
const { SDK_VERSION } = require('./gradle-config-defaults');
// Re-exporting these for backwards compatibility and for unit testing.
@@ -44,6 +44,31 @@ 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
@@ -61,19 +86,36 @@ 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 () {
var androidStudioPath;
var i = 0;
var foundStudio = false;
var program_dir;
let androidStudioPath;
let i = 0;
let foundStudio = false;
let 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()) {
var result = execa.sync(path.join(__dirname, 'getASPath.bat'));
const 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) {
var androidPath = path.join(process.env.ProgramFiles, 'Android') + '/';
const androidPath = path.join(process.env.ProgramFiles, 'Android') + '/';
if (fs.existsSync(androidPath)) {
program_dir = fs.readdirSync(androidPath);
while (i < program_dir.length && !foundStudio) {
@@ -92,7 +134,7 @@ module.exports.get_gradle_wrapper = function () {
}
if (androidStudioPath !== null && fs.existsSync(androidStudioPath)) {
var dirs = fs.readdirSync(androidStudioPath);
const dirs = fs.readdirSync(androidStudioPath);
if (dirs[0].split('-')[0] === 'gradle') {
return path.join(androidStudioPath, dirs[0], 'bin', 'gradle');
}
@@ -104,13 +146,13 @@ module.exports.get_gradle_wrapper = function () {
// Returns a promise. Called only by build and clean commands.
module.exports.check_gradle = function () {
var sdkDir = process.env.ANDROID_SDK_ROOT || process.env.ANDROID_HOME;
const sdkDir = process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT;
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_SDK_ROOT\' env variable.'));
}
var gradlePath = module.exports.get_gradle_wrapper();
const gradlePath = module.exports.get_gradle_wrapper();
if (gradlePath.length !== 0) return Promise.resolve(gradlePath);
@@ -132,19 +174,20 @@ 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_SDK_ROOT = value;
process.env.ANDROID_HOME = value;
}
}
var adbInPath = forgivingWhichSync('adb');
var avdmanagerInPath = forgivingWhichSync('avdmanager');
var hasAndroidHome = false;
const adbInPath = forgivingWhichSync('adb');
const avdmanagerInPath = forgivingWhichSync('avdmanager');
if (process.env.ANDROID_SDK_ROOT) {
maybeSetAndroidHome(path.resolve(process.env.ANDROID_SDK_ROOT));
if (process.env.ANDROID_HOME) {
maybeSetAndroidHome(path.resolve(process.env.ANDROID_HOME));
}
// First ensure ANDROID_HOME is set
@@ -197,15 +240,15 @@ module.exports.check_android = function () {
}
if (!hasAndroidHome) {
// 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 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 (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_SDK_ROOT\' environment variable. Try setting it manually.\n' +
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
'Detected \'adb\' command at ' + parentDir + ' but no \'platform-tools\' directory found near.\n' +
'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'platform-tools directory.');
}
@@ -216,26 +259,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_SDK_ROOT\' environment variable. Try setting it manually.\n' +
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
'Detected \'avdmanager\' command at ' + parentDir + ' but no \'tools' + path.sep + 'bin\' directory found near.\n' +
'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'tools' + path.sep + 'bin directory.');
}
}
}
if (!process.env.ANDROID_SDK_ROOT) {
throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' environment variable. Try setting it manually.\n' +
if (!process.env.ANDROID_HOME) {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
'Failed to find \'android\' command in your \'PATH\'. Try update your \'PATH\' to include path to valid SDK directory.');
}
if (!fs.existsSync(process.env.ANDROID_SDK_ROOT)) {
throw new CordovaError('\'ANDROID_SDK_ROOT\' environment variable is set to non-existent path: ' + process.env.ANDROID_SDK_ROOT +
if (!fs.existsSync(process.env.ANDROID_HOME)) {
throw new CordovaError('\'ANDROID_HOME\' 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_SDK_ROOT, 'platform-tools');
process.env.PATH += path.delimiter + path.join(process.env.ANDROID_HOME, 'platform-tools');
}
if (hasAndroidHome && !avdmanagerInPath) {
process.env.PATH += path.delimiter + path.join(process.env.ANDROID_SDK_ROOT, 'tools', 'bin');
process.env.PATH += path.delimiter + path.join(process.env.ANDROID_HOME, 'tools', 'bin');
}
return hasAndroidHome;
});
@@ -247,7 +290,7 @@ module.exports.check_android_target = function (projectRoot) {
// android-L
// Google Inc.:Google APIs:20
// Google Inc.:Glass Development Kit Preview:20
var desired_api_level = module.exports.get_target(projectRoot);
const 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;
@@ -259,8 +302,8 @@ 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_SDK_ROOT=' + process.env.ANDROID_SDK_ROOT + ' (recommended setting)');
console.log('ANDROID_HOME=' + process.env.ANDROID_HOME + ' (DEPRECATED)');
console.log('ANDROID_HOME=' + process.env.ANDROID_HOME + ' (recommended setting)');
console.log('ANDROID_SDK_ROOT=' + process.env.ANDROID_SDK_ROOT + ' (DEPRECATED)');
return Promise.all([this.check_java(), this.check_android()]).then(function (values) {
console.log('Using Android SDK: ' + process.env.ANDROID_SDK_ROOT);
@@ -279,7 +322,7 @@ 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
*/
var Requirement = function (id, name, version, installed) {
const Requirement = function (id, name, version, installed) {
this.id = id;
this.name = name;
this.installed = installed || false;
@@ -296,14 +339,14 @@ var Requirement = function (id, name, version, installed) {
* @return Promise<Requirement[]> Array of requirements. Due to implementation, promise is always fulfilled.
*/
module.exports.check_all = function (projectRoot) {
var requirements = [
const requirements = [
new Requirement('java', 'Java JDK'),
new Requirement('androidSdk', 'Android SDK'),
new Requirement('androidTarget', 'Android target'),
new Requirement('gradle', 'Gradle')
];
var checkFns = [
const checkFns = [
this.check_java,
this.check_android,
this.check_android_target.bind(this, projectRoot),
@@ -313,7 +356,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
var requirement = requirements[idx];
const requirement = requirements[idx];
return promise.then(checkFn).then(function (version) {
requirement.installed = true;
requirement.metadata.version = version;

View File

@@ -17,15 +17,15 @@
under the License.
*/
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 path = require('path');
const fs = require('fs-extra');
const utils = require('./utils');
const check_reqs = require('./check_reqs');
const ROOT = path.join(__dirname, '..');
const { createEditor } = require('properties-parser');
var CordovaError = require('cordova-common').CordovaError;
var AndroidManifest = require('./AndroidManifest');
const CordovaError = require('cordova-common').CordovaError;
const 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
@@ -37,16 +37,15 @@ 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) {
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 nestedCordovaLibPath = getFrameworkDir(projectPath, false);
const srcCordovaJsPath = path.join(ROOT, 'templates', 'project', 'assets', 'www', 'cordova.js');
const 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 +56,7 @@ function copyJsAndLibrary (projectPath, shared, projectName, targetAPI) {
fs.copySync(srcCordovaJsPath, path.join(platform_www, 'cordova.js'));
if (shared) {
var relativeFrameworkPath = path.relative(projectPath, getFrameworkDir(projectPath, true));
const relativeFrameworkPath = path.relative(projectPath, getFrameworkDir(projectPath, true));
fs.symlinkSync(relativeFrameworkPath, nestedCordovaLibPath, 'dir');
} else {
fs.ensureDirSync(nestedCordovaLibPath);
@@ -74,9 +73,9 @@ function copyJsAndLibrary (projectPath, shared, projectName, targetAPI) {
}
function extractSubProjectPaths (data) {
var ret = {};
var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg;
var m;
const ret = {};
const r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg;
let m;
while ((m = r.exec(data))) {
ret[m[1]] = 1;
}
@@ -84,13 +83,13 @@ function extractSubProjectPaths (data) {
}
function writeProjectProperties (projectPath, target_api) {
var dstPath = path.join(projectPath, 'project.properties');
var templatePath = path.join(ROOT, 'templates', 'project', 'project.properties');
var srcPath = fs.existsSync(dstPath) ? dstPath : templatePath;
const dstPath = path.join(projectPath, 'project.properties');
const templatePath = path.join(ROOT, 'templates', 'project', 'project.properties');
const srcPath = fs.existsSync(dstPath) ? dstPath : templatePath;
var data = fs.readFileSync(srcPath, 'utf8');
let data = fs.readFileSync(srcPath, 'utf8');
data = data.replace(/^target=.*/m, 'target=' + target_api);
var subProjects = extractSubProjectPaths(data);
let subProjects = extractSubProjectPaths(data);
subProjects = subProjects.filter(function (p) {
return !(/^CordovaLib$/m.exec(p) ||
/[\\/]cordova-android[\\/]framework$/m.exec(p) ||
@@ -101,7 +100,7 @@ function writeProjectProperties (projectPath, target_api) {
if (!/\n$/.exec(data)) {
data += '\n';
}
for (var i = 0; i < subProjects.length; ++i) {
for (let i = 0; i < subProjects.length; ++i) {
data += 'android.library.reference.' + (i + 1) + '=' + subProjects[i] + '\n';
}
fs.writeFileSync(dstPath, data);
@@ -109,12 +108,12 @@ function writeProjectProperties (projectPath, target_api) {
// This makes no sense, what if you're building with a different build system?
function prepBuildFiles (projectPath) {
var buildModule = require('./builders/builders');
const buildModule = require('./builders/builders');
buildModule.getBuilder(projectPath).prepBuildFiles();
}
function copyBuildRules (projectPath, isLegacy) {
var srcDir = path.join(ROOT, 'templates', 'project');
const 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 +129,8 @@ function copyBuildRules (projectPath, isLegacy) {
}
function copyScripts (projectPath) {
var srcScriptsDir = path.join(ROOT, 'templates', 'cordova');
var destScriptsDir = path.join(projectPath, 'cordova');
const srcScriptsDir = path.join(ROOT, 'templates', 'cordova');
const destScriptsDir = path.join(projectPath, 'cordova');
// Delete old scripts directory if this is an update.
fs.removeSync(destScriptsDir);
// Copy in the new ones.
@@ -147,7 +146,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
var msg = 'Error validating package name. ';
const 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 +166,7 @@ function validatePackageName (package_name) {
* otherwise.
*/
function validateProjectName (project_name) {
var msg = 'Error validating project name. ';
const 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,19 +175,6 @@ 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.
*
@@ -211,36 +197,37 @@ 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 || 'CordovaExample'));
project_path = path.relative(process.cwd(), project_path);
// Check if project already exists
if (fs.existsSync(project_path)) {
return Promise.reject(new CordovaError('Project already exists! Delete and recreate'));
}
var package_name = config.android_packageName() || config.packageName() || 'my.cordova.project';
var project_name = config.name()
? config.name().replace(/[^\w.]/g, '_') : 'CordovaExample';
const package_name = config.android_packageName() || config.packageName() || 'io.cordova.helloCordova';
const project_name = config.name() || 'Hello Cordova';
var safe_activity_name = config.android_activityName() || options.activityName || 'MainActivity';
var target_api = check_reqs.get_target(project_path);
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);
// 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: ' + target_api);
events.emit('log', '\tAndroid Target SDK: ' + target_api);
events.emit('log', '\tAndroid Compile SDK: ' + compile_api);
events.emit('verbose', 'Copying android template project to ' + project_path);
var project_template_dir = options.customTemplate || path.join(ROOT, 'templates', 'project');
var app_path = path.join(project_path, 'app', 'src', 'main');
const project_template_dir = options.customTemplate || path.join(ROOT, 'templates', 'project');
const app_path = path.join(project_path, 'app', 'src', 'main');
// copy project template
fs.ensureDirSync(app_path);
@@ -255,29 +242,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
var java_path = path.join(app_path, 'java');
var assets_path = path.join(app_path, 'assets');
var resource_path = path.join(app_path, 'res');
const java_path = path.join(app_path, 'java');
const assets_path = path.join(app_path, 'assets');
const resource_path = path.join(app_path, 'res');
fs.ensureDirSync(java_path);
fs.ensureDirSync(assets_path);
fs.ensureDirSync(resource_path);
// interpolate the activity name and package
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');
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');
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__/, project_name);
utils.replaceFileContents(path.join(app_path, 'res', 'values', 'strings.xml'), /__NAME__/, utils.escape(project_name));
utils.replaceFileContents(activity_path, /__ID__/, package_name);
var manifest = new AndroidManifest(path.join(project_template_dir, 'AndroidManifest.xml'));
const manifest = new AndroidManifest(path.join(project_template_dir, 'AndroidManifest.xml'));
manifest.setPackageId(package_name)
.getActivity().setName(safe_activity_name);
var manifest_path = path.join(app_path, 'AndroidManifest.xml');
const manifest_path = path.join(app_path, 'AndroidManifest.xml');
manifest.write(manifest_path);
exports.copyScripts(project_path);
@@ -286,14 +273,13 @@ 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) {
var pkg = require('../package');
var msg = 'Android project ' + (type === 'update' ? 'updated ' : 'created ') + 'with ' + pkg.name + '@' + pkg.version;
const pkg = require('../package');
let msg = 'Android project ' + (type === 'update' ? 'updated ' : 'created ') + 'with ' + pkg.name + '@' + pkg.version;
if (link) {
msg += ' and has a linked CordovaLib';
}
@@ -302,7 +288,7 @@ function generateDoneMessage (type, link) {
// Returns a promise.
exports.update = function (projectPath, options, events) {
var errorString =
const 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');
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');
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');
// 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 }) => {
var response = output.split('\n');
var emulator_list = [];
for (var i = 1; i < response.length; i++) {
const response = output.split('\n');
const emulator_list = [];
for (let i = 1; i < response.length; i++) {
// To return more detailed information use img_obj
var img_obj = {};
const 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();
}
}
var version_string = img_obj.target.replace(/Android\s+/, '');
const version_string = img_obj.target.replace(/Android\s+/, '');
var api_level = android_sdk.version_string_to_api_level[version_string];
const 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) {
var api_level = avd.target.match(/\d+/);
const api_level = avd.target.match(/\d+/);
if (api_level) {
var level = android_versions.get(api_level);
const 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;
var closest = 9999;
var best = images[0];
for (var i in images) {
var target = images[i].target;
let closest = 9999;
let best = images[0];
for (const i in images) {
const target = images[i].target;
if (target && target.indexOf('API level') > -1) {
var num = parseInt(target.split('(API level ')[1].replace(')', ''));
const 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 () {
var self = this;
const self = this;
return self.list_started().then(function (emulators) {
for (var p = 5584; p >= 5554; p -= 2) {
for (let 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) {
var self = this;
const 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
var emulator_dir = path.dirname(which.sync('emulator'));
var args = ['-avd', emulatorId, '-port', port];
const emulator_dir = path.dirname(which.sync('emulator'));
const 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) {
var self = this;
const self = this;
return Promise.resolve().then(function () {
var emulator_id = 'emulator-' + port;
const 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) {
var self = this;
const 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,8 +98,9 @@ const java = {
}
} else {
// See if we can derive it from javac's location.
var maybeJavaHome = path.dirname(path.dirname(javacPath));
if (fs.existsSync(path.join(maybeJavaHome, 'lib', 'tools.jar'))) {
const maybeJavaHome = path.dirname(path.dirname(javacPath));
if (fs.existsSync(path.join(maybeJavaHome, 'bin', 'java')) ||
fs.existsSync(path.join(maybeJavaHome, 'bin', 'java.exe'))) {
environment.JAVA_HOME = maybeJavaHome;
} else {
throw new CordovaError(default_java_error_msg);

View File

@@ -14,19 +14,19 @@
*
*/
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 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 handlers = {
const 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));
var dest = getInstallDestination(obj);
const dest = getInstallDestination(obj);
if (options && options.force) {
copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
@@ -35,7 +35,7 @@ var handlers = {
}
},
uninstall: function (obj, plugin, project, options) {
var dest = getInstallDestination(obj);
const 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 @@ var handlers = {
},
'lib-file': {
install: function (obj, plugin, project, options) {
var dest = path.join('app/libs', path.basename(obj.src));
const 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) {
var dest = path.join('app/libs', path.basename(obj.src));
const dest = path.join('app/libs', path.basename(obj.src));
removeFile(path.resolve(project.projectDir, dest));
}
},
'resource-file': {
install: function (obj, plugin, project, options) {
var dest = path.join('app', 'src', 'main', obj.target);
const 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) {
var dest = path.join('app', 'src', 'main', obj.target);
const dest = path.join('app', 'src', 'main', obj.target);
removeFile(path.resolve(project.projectDir, dest));
}
},
framework: {
install: function (obj, plugin, project, options) {
var src = obj.src;
const src = obj.src;
if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
events.emit('verbose', 'Installing Android library: ' + src);
var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
var subDir;
const parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
let subDir;
if (obj.custom) {
var subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
const 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 @@ var handlers = {
}
},
uninstall: function (obj, plugin, project, options) {
var src = obj.src;
const src = obj.src;
if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
events.emit('verbose', 'Uninstalling Android library: ' + src);
var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
var subDir;
const parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
let subDir;
if (obj.custom) {
var subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
const 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.
var parDir = path.dirname(subDir);
const parDir = path.dirname(subDir);
if (fs.existsSync(parDir) && fs.readdirSync(parDir).length === 0) {
fs.rmdirSync(parDir);
}
@@ -139,7 +139,7 @@ var handlers = {
}
},
uninstall: function (obj, plugin, project, options) {
var target = obj.target || obj.src;
const target = obj.target || obj.src;
if (!target) throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
@@ -155,29 +155,29 @@ var handlers = {
'js-module': {
install: function (obj, plugin, project, options) {
// Copy the plugin's files into the www directory.
var moduleSource = path.resolve(plugin.dir, obj.src);
var moduleName = plugin.id + '.' + (obj.name || path.basename(obj.src, path.extname(obj.src)));
const moduleSource = path.resolve(plugin.dir, obj.src);
const moduleName = plugin.id + '.' + (obj.name || path.basename(obj.src, path.extname(obj.src)));
// Read in the file, prepend the cordova.define, and write it back out.
var scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM
let 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';
var wwwDest = path.resolve(project.www, 'plugins', plugin.id, obj.src);
const 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
var platformWwwDest = path.resolve(project.platformWww, 'plugins', plugin.id, obj.src);
const 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) {
var pluginRelativePath = path.join('plugins', plugin.id, obj.src);
const 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
var real_path = fs.realpathSync(src);
var real_plugin_path = fs.realpathSync(plugin_dir);
const real_path = fs.realpathSync(src);
const 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) {
var target_path = path.resolve(project_dir, dest);
const 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 || '.';
var file = path.resolve(baseDir, destFile);
const file = path.resolve(baseDir, destFile);
if (!fs.existsSync(file)) return;
removeFile(file);
// check if directory is empty
var curDir = path.dirname(file);
let 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) {
var APP_MAIN_PREFIX = 'app/src/main';
var PATH_SEPARATOR = '/';
const APP_MAIN_PREFIX = 'app/src/main';
const PATH_SEPARATOR = '/';
var PATH_SEP_MATCH = '\\' + PATH_SEPARATOR;
var PATH_SEP_OR_EOL_MATCH = '(\\' + PATH_SEPARATOR + '|$)';
const PATH_SEP_MATCH = '\\' + PATH_SEPARATOR;
const PATH_SEP_OR_EOL_MATCH = '(\\' + PATH_SEPARATOR + '|$)';
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);
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);
if (appReg.test(obj.targetDir)) {
// If any source file is using the new app directory structure,

View File

@@ -17,23 +17,22 @@
under the License.
*/
var fs = require('fs-extra');
var path = require('path');
const fs = require('fs-extra');
const path = require('path');
const nopt = require('nopt');
const glob = require('fast-glob');
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 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;
const utils = require('./utils');
const gradleConfigDefaults = require('./gradle-config-defaults');
const checkReqs = require('./check_reqs');
const GradlePropertiesParser = require('./config/GradlePropertiesParser');
function parseArguments (argv) {
@@ -44,15 +43,15 @@ function parseArguments (argv) {
}
module.exports.prepare = function (cordovaProject, options) {
var self = this;
const self = this;
let args = {};
if (options && options.options) {
args = parseArguments(options.options.argv);
}
var platformJson = PlatformJson.load(this.locations.root, this.platform);
var munger = new PlatformMunger(this.platform, this.locations.root, platformJson, new PluginInfoProvider());
const platformJson = PlatformJson.load(this.locations.root, this.platform);
const munger = new PlatformMunger(this.platform, this.locations.root, platformJson, new PluginInfoProvider());
this._config = updateConfigFilesFrom(cordovaProject.projectConfig, munger, this.locations);
@@ -63,16 +62,15 @@ 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(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');
});
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');
});
};
/** @param {PlatformApi} project */
@@ -83,6 +81,14 @@ 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 });
@@ -93,6 +99,7 @@ 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 },
@@ -151,19 +158,18 @@ 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.
var projectRoot = path.resolve(this.root, '../..');
const projectRoot = path.resolve(this.root, '../..');
if ((options && options.noPrepare) || !fs.existsSync(this.locations.configXml) ||
!fs.existsSync(this.locations.configXml)) {
return Promise.resolve();
}
var projectConfig = new ConfigParser(this.locations.configXml);
const projectConfig = new ConfigParser(this.locations.configXml);
var self = this;
const 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));
});
};
@@ -195,7 +201,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
var config = new ConfigParser(locations.configXml);
const config = new ConfigParser(locations.configXml);
xmlHelpers.mergeXml(sourceConfig.doc.getroot(),
config.doc.getroot(), 'android', /* clobber= */true);
@@ -220,19 +226,19 @@ function logFileOp (message) {
* paths for www files.
*/
function updateWww (cordovaProject, destinations) {
var sourceDirs = [
const 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
var merges_path = path.join(cordovaProject.root, 'merges', 'android');
const 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'));
}
var targetDir = path.relative(cordovaProject.root, destinations.www);
const targetDir = path.relative(cordovaProject.root, destinations.www);
events.emit(
'verbose', 'Merging and updating files from [' + sourceDirs.join(', ') + '] to ' + targetDir);
FileUpdater.mergeAndUpdateDir(
@@ -243,7 +249,7 @@ function updateWww (cordovaProject, destinations) {
* Cleans all files from the platform 'www' directory.
*/
function cleanWww (projectRoot, locations) {
var targetDir = path.relative(projectRoot, locations.www);
const targetDir = path.relative(projectRoot, locations.www);
events.emit('verbose', 'Cleaning ' + targetDir);
// No source paths are specified, so mergeAndUpdateDir() will clear the target directory.
@@ -259,25 +265,21 @@ function cleanWww (projectRoot, locations) {
* @param {Object} locations A map of locations for this platform
*/
function updateProjectAccordingTo (platformConfig, locations) {
// Update app name by editing res/values/strings.xml
var strings = xmlHelpers.parseElementtreeSync(locations.strings);
updateProjectStrings(platformConfig, locations);
updateProjectSplashScreen(platformConfig, locations);
var name = platformConfig.name();
strings.find('string[@name="app_name"]').text = name.replace(/'/g, '\\\'');
const name = platformConfig.name();
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);
// 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');
// Java packages cannot support dashes
var androidPkgName = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_');
const androidPkgName = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_');
var manifest = new AndroidManifest(locations.manifest);
var manifestId = manifest.getPackageId();
const manifest = new AndroidManifest(locations.manifest);
const manifestId = manifest.getPackageId();
manifest.getActivity()
.setOrientation(platformConfig.getPreference('orientation'))
@@ -301,27 +303,23 @@ 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 = java_files[0];
const destFile = path.normalize(java_files[0]);
// 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 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);
// If package was name changed we need to remove old java with main activity
fs.removeSync(java_files[0]);
events.emit('verbose', `remove ${destFile}`);
fs.removeSync(destFile);
// remove any empty directories
var currentDir = path.dirname(java_files[0]);
var sourcesRoot = path.resolve(locations.root, 'src');
let currentDir = path.dirname(destFile);
const sourcesRoot = path.resolve(locations.root, 'src');
while (currentDir !== sourcesRoot) {
if (fs.existsSync(currentDir) && fs.readdirSync(currentDir).length === 0) {
fs.rmdirSync(currentDir);
@@ -333,12 +331,286 @@ 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',
'windowSplashScreenBrandingImage',
'windowSplashScreenIconBackgroundColor',
'postSplashScreenTheme'
].forEach(themeKey => {
const cdvConfigPrefKey = 'Android' + themeKey.charAt(0).toUpperCase() + themeKey.slice(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 '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);
} else if (cdvConfigPrefValue) {
// if there is no current node, create a new node.
if (!themeTargetNode) {
themeTargetNode = themes.getroot().makeelement('item', { name: themeKey });
splashScreenTheme.append(themeTargetNode);
}
// 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',
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 !== '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 === '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) {
var nums = version.split('-')[0].split('.');
var versionCode = 0;
const nums = version.split('-')[0].split('.');
let versionCode = 0;
if (+nums[0]) {
versionCode += +nums[0] * 10000;
}
@@ -356,7 +628,8 @@ 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);
@@ -366,72 +639,10 @@ function getAdaptiveImageResourcePath (resourcesDir, type, density, name, source
if (/\.9\.png$/.test(sourceName)) {
name = name.replace(/\.png$/, '.9.png');
}
var resourcePath = path.join(resourcesDir, (density ? type + '-' + density + '-v26' : type), name);
const 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');
@@ -511,7 +722,7 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
const android_icons = preparedIcons.android_icons;
const default_icon = preparedIcons.default_icon;
// The source paths for icons and splashes are relative to
// The source paths for icons are relative to
// project's config.xml location, so we use it as base path.
let background;
let foreground;
@@ -610,16 +821,16 @@ function updateIconResourceForLegacy (preparedIcons, resourceMap, platformResour
const android_icons = preparedIcons.android_icons;
const default_icon = preparedIcons.default_icon;
// The source paths for icons and splashes are relative to
// The source paths for icons are relative to
// project's config.xml location, so we use it as base path.
for (var density in android_icons) {
var targetPath = getImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher', path.basename(android_icons[density].src));
for (const density in android_icons) {
const 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) {
var defaultTargetPath = getImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher', path.basename(default_icon.src));
const defaultTargetPath = getImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher', path.basename(default_icon.src));
resourceMap[defaultTargetPath] = default_icon.src;
}
@@ -642,14 +853,14 @@ function prepareIcons (icons) {
// find the best matching icon for a given density or size
// @output android_icons
var parseIcon = function (icon, icon_size) {
const parseIcon = function (icon, icon_size) {
// do I have a platform icon for that density already
var density = icon.density || SIZE_TO_DENSITY_MAP[icon_size];
const density = icon.density || SIZE_TO_DENSITY_MAP[icon_size];
if (!density) {
// invalid icon defition ( or unsupported size)
return;
}
var previous = android_icons[density];
const previous = android_icons[density];
if (previous && previous.platform) {
return;
}
@@ -657,9 +868,9 @@ function prepareIcons (icons) {
};
// iterate over all icon elements to find the default icon and call parseIcon
for (var i = 0; i < icons.length; i++) {
var icon = icons[i];
var size = icon.width;
for (let i = 0; i < icons.length; i++) {
const icon = icons[i];
let size = icon.width;
if (!size) {
size = icon.height;
@@ -703,7 +914,7 @@ function prepareIcons (icons) {
}
function cleanIcons (projectRoot, projectConfig, platformResourcesDir) {
var icons = projectConfig.getIcons('android');
const icons = projectConfig.getIcons('android');
// Skip if there are no app defined icons in config.xml
if (icons.length === 0) {
@@ -740,18 +951,8 @@ 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) {
var files = cordovaProject.projectConfig.getFileResources('android');
const files = cordovaProject.projectConfig.getFileResources('android');
// if there are resource-file elements in config.xml
if (files.length === 0) {
@@ -759,9 +960,9 @@ function updateFileResources (cordovaProject, platformDir) {
return;
}
var resourceMap = {};
const resourceMap = {};
files.forEach(function (res) {
var targetPath = path.join(platformDir, res.target);
const targetPath = path.join(platformDir, res.target);
resourceMap[targetPath] = res.src;
});
@@ -771,13 +972,13 @@ function updateFileResources (cordovaProject, platformDir) {
}
function cleanFileResources (projectRoot, projectConfig, platformDir) {
var files = projectConfig.getFileResources('android', true);
const files = projectConfig.getFileResources('android', true);
if (files.length > 0) {
events.emit('verbose', 'Cleaning resource files at ' + platformDir);
var resourceMap = {};
const resourceMap = {};
files.forEach(function (res) {
var filePath = path.join(platformDir, res.target);
const filePath = path.join(platformDir, res.target);
resourceMap[filePath] = null;
});
@@ -798,14 +999,14 @@ function cleanFileResources (projectRoot, projectConfig, platformDir) {
* 'singleTop'
*/
function findAndroidLaunchModePreference (platformConfig) {
var launchMode = platformConfig.getPreference('AndroidLaunchMode');
const launchMode = platformConfig.getPreference('AndroidLaunchMode');
if (!launchMode) {
// Return a default value
return 'singleTop';
}
var expectedValues = ['standard', 'singleTop', 'singleTask', 'singleInstance'];
var valid = expectedValues.indexOf(launchMode) >= 0;
const expectedValues = ['standard', 'singleTop', 'singleTask', 'singleInstance'];
const 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';
var events = require('cordova-common').events;
const events = require('cordova-common').events;
/**
* Retry a promise-returning function a number of times, propagating its

View File

@@ -17,7 +17,7 @@
under the License.
*/
var emulator = require('./emulator');
const emulator = require('./emulator');
const target = require('./target');
const build = require('./build');
const PackageType = require('./PackageType');

View File

@@ -66,3 +66,21 @@ 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;

6861
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "cordova-android",
"version": "10.0.0",
"version": "11.0.0",
"description": "cordova-android release",
"main": "lib/Api.js",
"repository": "github:apache/cordova-android",
@@ -26,25 +26,26 @@
"android-versions": "^1.7.0",
"cordova-common": "^4.0.2",
"execa": "^5.1.1",
"fast-glob": "^3.2.7",
"fs-extra": "^10.0.0",
"fast-glob": "^3.2.11",
"fs-extra": "^10.1.0",
"is-path-inside": "^3.0.3",
"nopt": "^5.0.0",
"properties-parser": "^0.3.1",
"semver": "^7.3.5",
"semver": "^7.3.7",
"untildify": "^4.0.0",
"which": "^2.0.2"
},
"devDependencies": {
"@cordova/eslint-config": "^3.0.0",
"@cordova/eslint-config": "^4.0.0",
"cordova-js": "^6.1.0",
"jasmine": "^3.8.0",
"elementtree": "^0.1.7",
"jasmine": "^4.2.1",
"jasmine-spec-reporter": "^7.0.0",
"nyc": "^15.1.0",
"rewire": "^5.0.0"
"rewire": "^6.0.0"
},
"engines": {
"node": ">=12.0.0"
"node": ">=14.0.0"
},
"nyc": {
"include": [

View File

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

View File

@@ -17,12 +17,12 @@
under the License.
*/
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 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');
const {
SDK_VERSION: DEFAULT_TARGET_API
@@ -34,7 +34,7 @@ describe('check_reqs', function () {
check_reqs = rewire('../../lib/check_reqs');
});
var original_env;
let original_env;
beforeAll(function () {
original_env = Object.assign({}, process.env);
});
@@ -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_SDK_ROOT on Windows', () => {
it('it should set ANDROID_HOME 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_SDK_ROOT).toContain('windows-local-app-data');
expect(process.env.ANDROID_HOME).toContain('windows-local-app-data');
});
});
it('it should set ANDROID_SDK_ROOT on Darwin', () => {
it('it should set ANDROID_HOME 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_SDK_ROOT).toContain('home is where the heart is');
expect(process.env.ANDROID_HOME).toContain('home is where the heart is');
});
});
});
@@ -91,17 +91,17 @@ describe('check_reqs', function () {
return path;
});
});
it('should set ANDROID_SDK_ROOT based on `adb` command if command exists in a SDK-like directory structure', () => {
it('should set ANDROID_HOME 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 '/android/sdk/platform-tools/adb';
return path.normalize('/android/sdk/platform-tools/adb');
} else {
return null;
}
});
return check_reqs.check_android().then(function () {
expect(process.env.ANDROID_SDK_ROOT).toEqual('/android/sdk');
expect(process.env.ANDROID_HOME).toEqual(path.normalize('/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_SDK_ROOT based on `avdmanager` command if command exists in a SDK-like directory structure', () => {
it('should set ANDROID_HOME 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 '/android/sdk/tools/bin/avdmanager';
return path.normalize('/android/sdk/tools/bin/avdmanager');
} else {
return null;
}
});
return check_reqs.check_android().then(function () {
expect(process.env.ANDROID_SDK_ROOT).toEqual('/android/sdk');
expect(process.env.ANDROID_HOME).toEqual(path.normalize('/android/sdk'));
});
});
it('should error out if `avdmanager` command exists in a non-SDK-like directory structure', () => {
@@ -169,7 +169,7 @@ describe('check_reqs', function () {
it('should use ANDROID_SDK_ROOT if defined', () => {
spyOn(fs, 'existsSync').and.returnValue(true);
process.env.ANDROID_SDK_ROOT = '/android/sdk';
process.env.ANDROID_SDK_ROOT = path.normalize('/android/sdk');
return check_reqs.check_android().then(() => {
expect(process.env.ANDROID_SDK_ROOT).toContain(expectedAndroidSdkPath);
});
@@ -177,25 +177,25 @@ describe('check_reqs', function () {
it('should use ANDROID_HOME if defined and ANDROID_SDK_ROOT is not defined', () => {
spyOn(fs, 'existsSync').and.returnValue(true);
process.env.ANDROID_HOME = '/android/sdk';
process.env.ANDROID_HOME = path.normalize('/android/sdk');
return check_reqs.check_android().then(() => {
expect(process.env.ANDROID_SDK_ROOT).toContain(expectedAndroidSdkPath);
expect(process.env.ANDROID_HOME).toContain(expectedAndroidSdkPath);
});
});
it('should use ANDROID_SDK_ROOT if defined and ANDROID_HOME is defined', () => {
it('should use ANDROID_HOME if defined and ANDROID_SDK_ROOT is defined', () => {
spyOn(fs, 'existsSync').and.returnValue(true);
process.env.ANDROID_SDK_ROOT = '/android/sdk/root';
process.env.ANDROID_HOME = '/android/sdk';
process.env.ANDROID_SDK_ROOT = path.normalize('/android/sdk/root');
process.env.ANDROID_HOME = path.normalize('/android/sdk');
return check_reqs.check_android().then(() => {
expect(process.env.ANDROID_SDK_ROOT).toContain(expectedAndroidRootSdkPath);
});
});
it('should throw if ANDROID_SDK_ROOT points to an invalid path', () => {
process.env.ANDROID_SDK_ROOT = '/android/sdk';
it('should throw if ANDROID_HOME points to an invalid path', () => {
process.env.ANDROID_HOME = path.normalize('/android/sdk');
return check_reqs.check_android().catch((error) => {
expect(error.toString()).toContain('\'ANDROID_SDK_ROOT\' environment variable is set to non-existent path:');
expect(error.toString()).toContain('\'ANDROID_HOME\' 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_SDK_ROOT = 'let the children play';
process.env.ANDROID_HOME = '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', () => {
@@ -222,24 +222,24 @@ describe('check_reqs', function () {
delete process.env.ANDROID_SDK_ROOT;
delete process.env.ANDROID_HOME;
spyOn(check_reqs, 'get_gradle_wrapper').and.callFake(() => {
return (process.env.ANDROID_SDK_ROOT || process.env.ANDROID_HOME) + '/bin/gradle';
return path.normalize((process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT) + '/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');
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');
process.env.ANDROID_SDK_ROOT = path.normalize('/android/sdk/root');
process.env.ANDROID_HOME = path.normalize('/android/sdk/home');
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');
process.env.ANDROID_HOME = path.normalize('/android/sdk/home');
await expectAsync(check_reqs.check_gradle()).toBeResolvedTo(path.normalize('/android/sdk/home/bin/gradle'));
});
it('without ANDROID_SDK_ROOT / without ANDROID_HOME', () => {
@@ -250,7 +250,7 @@ describe('check_reqs', function () {
});
it('should error if sdk is installed but no gradle found', () => {
process.env.ANDROID_SDK_ROOT = '/android/sdk';
process.env.ANDROID_SDK_ROOT = path.normalize('/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';
var ConfigParser;
var getPreferenceSpy;
let ConfigParser;
let 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 () {
var target = check_reqs.get_target(projectRoot);
const 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));
var target = check_reqs.get_target(projectRoot);
const 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');
var target = check_reqs.get_target(projectRoot);
const 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));
var target = check_reqs.get_target(projectRoot);
const 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', () => {
var fake_targets = ['you are my fire', 'my one desire'];
const 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', () => {
var fake_targets = ['you are my fire', 'my one desire'];
const 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,17 +17,17 @@
under the License.
*/
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');
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');
describe('create', function () {
describe('validatePackageName helper method', function () {
describe('happy path (valid package names)', function () {
var valid = [
const valid = [
'org.apache.mobilespec',
'com.example',
'com.floors42.package',
@@ -82,7 +82,7 @@ describe('create', function () {
describe('validateProjectName helper method', function () {
describe('happy path (valid project names)', function () {
var valid = [
const valid = [
'mobilespec',
'package_name',
'PackageName',
@@ -111,14 +111,14 @@ describe('create', function () {
});
describe('main method', function () {
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';
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';
beforeEach(function () {
Manifest_mock.prototype = jasmine.createSpyObj('AndroidManifest instance mock', ['setPackageId', 'getActivity', 'setName', 'write']);
@@ -132,7 +132,6 @@ 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');
@@ -148,10 +147,10 @@ describe('create', function () {
});
describe('parameter values and defaults', function () {
it('should have a default package name of my.cordova.project', () => {
it('should have a default package name of io.cordova.helloCordova', () => {
config_mock.packageName.and.returnValue(undefined);
return create.create(project_path, config_mock, {}, events_mock).then(() => {
expect(create.validatePackageName).toHaveBeenCalledWith('my.cordova.project');
expect(create.validatePackageName).toHaveBeenCalledWith('io.cordova.helloCordova');
});
});
@@ -162,10 +161,10 @@ describe('create', function () {
});
});
it('should have a default project name of CordovaExample', () => {
it('should have a default project name of Hello Cordova', () => {
config_mock.name.and.returnValue(undefined);
return create.create(project_path, config_mock, {}, events_mock).then(() => {
expect(create.validateProjectName).toHaveBeenCalledWith('CordovaExample');
expect(create.validateProjectName).toHaveBeenCalledWith('Hello Cordova');
});
});
@@ -176,10 +175,10 @@ describe('create', function () {
});
});
it('should replace any non-word characters (including unicode and spaces) in the ConfigParser-provided project name with underscores', () => {
it('should keep non-word characters (including unicode and spaces) in the ConfigParser-provided project name', () => {
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 用用用用');
});
});
@@ -261,7 +260,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');
var activity_path = path.join(app_path, 'java', 'org', 'apache', 'cordova', 'CEEDEEVEE.java');
const 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');
@@ -276,6 +275,13 @@ 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);
@@ -301,24 +307,4 @@ 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

@@ -16,24 +16,24 @@
*
*/
var rewire = require('rewire');
var common = rewire('../../../lib/pluginHandlers');
var path = require('path');
var fs = require('fs-extra');
var osenv = require('os');
const rewire = require('rewire');
const common = rewire('../../../lib/pluginHandlers');
const path = require('path');
const fs = require('fs-extra');
const osenv = require('os');
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 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 copyFile = common.__get__('copyFile');
var deleteJava = common.__get__('deleteJava');
var copyNewFile = common.__get__('copyNewFile');
const copyFile = common.__get__('copyFile');
const deleteJava = common.__get__('deleteJava');
const 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');
var outside_file = '../non_plugin_file';
const 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');
var s = spyOn(fs, 'ensureDirSync').and.callThrough();
var resolvedDest = path.resolve(project_dir, dest);
const s = spyOn(fs, 'ensureDirSync').and.callThrough();
const 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');
var s = spyOn(fs, 'copySync').and.callThrough();
var resolvedDest = path.resolve(project_dir, dest);
const s = spyOn(fs, 'copySync').and.callThrough();
const 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 () {
var s = spyOn(fs, 'removeSync').and.callThrough();
const 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,34 +17,34 @@
under the License.
*/
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 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 PluginInfo = require('cordova-common').PluginInfo;
var AndroidProject = require('../../../lib/AndroidProject');
const PluginInfo = require('cordova-common').PluginInfo;
const AndroidProject = require('../../../lib/AndroidProject');
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');
var faultyPluginInfo = new PluginInfo(faultyplugin);
var invalid_source = faultyPluginInfo.getSourceFiles('android');
const faultyPluginInfo = new PluginInfo(faultyplugin);
const invalid_source = faultyPluginInfo.getSourceFiles('android');
describe('android project handler', function () {
describe('installation', function () {
var copyFileOrig = common.__get__('copyFile');
var copyFileSpy = jasmine.createSpy('copyFile');
var dummyProject;
const copyFileOrig = common.__get__('copyFile');
const copyFileSpy = jasmine.createSpy('copyFile');
let dummyProject;
beforeEach(function () {
fs.ensureDirSync(temp);
@@ -177,10 +177,10 @@ describe('android project handler', function () {
});
describe('of <framework> elements', function () {
var someString = jasmine.any(String);
const someString = jasmine.any(String);
var copyNewFileOrig = common.__get__('copyNewFile');
var copyNewFileSpy = jasmine.createSpy('copyNewFile');
const copyNewFileOrig = common.__get__('copyNewFile');
const copyNewFileSpy = jasmine.createSpy('copyNewFile');
beforeEach(function () {
fs.copySync(android_studio_project, temp);
@@ -200,34 +200,34 @@ describe('android project handler', function () {
});
it('Test#008 : should install framework without "parent" attribute into project root', function () {
var framework = { src: 'plugin-lib' };
const 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 () {
var childFramework = { src: 'plugin-lib2', parent: 'plugin-lib' };
const 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 () {
var framework = { src: 'plugin-lib' };
var cpSpy = spyOn(fs, 'copySync');
const framework = { src: 'plugin-lib' };
const 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 () {
var framework = { src: 'plugin-lib', custom: true };
const 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 () {
var framework = { src: 'plugin-lib', custom: true, type: 'gradleReference' };
const 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);
@@ -235,8 +235,8 @@ describe('android project handler', function () {
});
describe('of <js-module> elements', function () {
var jsModule = { src: 'www/dummyplugin.js' };
var wwwDest, platformWwwDest;
const jsModule = { src: 'www/dummyplugin.js' };
let wwwDest, platformWwwDest;
beforeEach(function () {
spyOn(fs, 'writeFileSync');
@@ -258,7 +258,7 @@ describe('android project handler', function () {
});
describe('of <asset> elements', function () {
var asset;
let asset;
beforeEach(function () {
asset = { src: 'www/dummyPlugin.js', target: 'foo/dummy.js' };
@@ -279,10 +279,10 @@ describe('android project handler', function () {
});
describe('uninstallation', function () {
var deleteJavaOrig = common.__get__('deleteJava');
const deleteJavaOrig = common.__get__('deleteJava');
const originalRemoveSync = fs.removeSync;
var deleteJavaSpy = jasmine.createSpy('deleteJava');
var dummyProject;
const deleteJavaSpy = jasmine.createSpy('deleteJava');
let dummyProject;
let removeSyncSpy;
beforeEach(function () {
@@ -385,7 +385,7 @@ describe('android project handler', function () {
});
describe('of <framework> elements', function () {
var someString = jasmine.any(String);
const someString = jasmine.any(String);
beforeEach(function () {
fs.ensureDirSync(path.join(dummyProject.projectDir, dummyPluginInfo.id));
@@ -400,26 +400,26 @@ describe('android project handler', function () {
});
it('Test#021 : should uninstall framework without "parent" attribute into project root', function () {
var framework = { src: 'plugin-lib' };
const 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 () {
var childFramework = { src: 'plugin-lib2', parent: 'plugin-lib' };
const 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 () {
var framework = { src: 'plugin-lib', custom: true };
const 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 () {
var framework = { src: 'plugin-lib', custom: true, type: 'gradleReference' };
const 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);
@@ -427,15 +427,15 @@ describe('android project handler', function () {
});
describe('of <js-module> elements', function () {
var jsModule = { src: 'www/dummyPlugin.js' };
var wwwDest;
var platformWwwDest;
const jsModule = { src: 'www/dummyPlugin.js' };
let wwwDest;
let platformWwwDest;
beforeEach(function () {
wwwDest = path.resolve(dummyProject.www, 'plugins', dummyPluginInfo.id, jsModule.src);
platformWwwDest = path.resolve(dummyProject.platformWww, 'plugins', dummyPluginInfo.id, jsModule.src);
var existsSyncOrig = fs.existsSync;
const existsSyncOrig = fs.existsSync;
spyOn(fs, 'existsSync').and.callFake(function (file) {
if ([wwwDest, platformWwwDest].indexOf(file) >= 0) return true;
return existsSyncOrig.call(fs, file);
@@ -456,14 +456,14 @@ describe('android project handler', function () {
});
describe('of <asset> elements', function () {
var asset = { src: 'www/dummyPlugin.js', target: 'foo/dummy.js' };
var wwwDest, platformWwwDest;
const asset = { src: 'www/dummyPlugin.js', target: 'foo/dummy.js' };
let wwwDest, platformWwwDest;
beforeEach(function () {
wwwDest = path.resolve(dummyProject.www, asset.target);
platformWwwDest = path.resolve(dummyProject.platformWww, asset.target);
var existsSyncOrig = fs.existsSync;
const 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,10 +17,12 @@
under the License.
*/
var rewire = require('rewire');
var path = require('path');
var CordovaError = require('cordova-common').CordovaError;
const rewire = require('rewire');
const path = require('path');
const CordovaError = require('cordova-common').CordovaError;
const GradlePropertiesParser = require('../../lib/config/GradlePropertiesParser');
const utils = require('../../lib/utils');
const et = require('elementtree');
const PATH_RESOURCE = path.join('platforms', 'android', 'app', 'src', 'main', 'res');
@@ -81,18 +83,6 @@ 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;
@@ -688,7 +678,7 @@ describe('prepare', () => {
};
const platformResourcesDir = PATH_RESOURCE;
var expectedResourceMapBackground = createResourceMap('ic_launcher_background.png');
const expectedResourceMapBackground = createResourceMap('ic_launcher_background.png');
// mocking initial responses for mapImageResources
prepare.__set__('mapImageResources', function (rootDir, subDir, type, resourceName) {
@@ -719,7 +709,7 @@ describe('prepare', () => {
};
const platformResourcesDir = PATH_RESOURCE;
var expectedResourceMap = createResourceMap();
const expectedResourceMap = createResourceMap();
// mocking initial responses for mapImageResources
prepare.__set__('mapImageResources', function (rootDir, subDir, type, resourceName) {
@@ -776,8 +766,9 @@ 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')
@@ -810,109 +801,154 @@ describe('prepare', () => {
});
});
describe('updateSplashes method', function () {
describe('relocate CordovaActivity class java file', () => {
// Rewire
let Api;
let api;
let prepare;
// Spies
let replaceFileContents;
let ensureDirSyncSpy;
let copySyncSpy;
let removeSyncSpy;
// Mock Data
let cordovaProject;
let platformResourcesDir;
let options;
let packageName;
let initialJavaActivityPath;
beforeEach(() => {
Api = rewire('../../lib/Api');
prepare = rewire('../../lib/prepare');
beforeEach(function () {
cordovaProject = {
root: '/mock',
projectConfig: {
path: '/mock/config.xml',
cdvNamespacePrefix: 'cdv'
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'
},
locations: {
plugins: '/mock/plugins',
www: '/mock/www'
www: '/mock/www',
strings: '/mock/res/values/strings.xml'
}
};
platformResourcesDir = PATH_RESOURCE;
// 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
api = new Api('android', cordovaProject.root);
initialJavaActivityPath = path.join(api.locations.javaSrc, 'com/company/product/MainActivity.java');
options = {
options: {}
};
Api.__set__('ConfigParser',
jasmine.createSpy('ConfigParser')
.and.returnValue(cordovaProject.projectConfig)
);
Api.__set__('prepare', prepare.prepare);
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({
setPackageId: jasmine.createSpy('setPackageId').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')
});
});
it('Test#001 : Should detect no defined splash screens.', function () {
const updateSplashes = prepare.__get__('updateSplashes');
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');
// mock data.
cordovaProject.projectConfig.getSplashScreens = function (platform) {
return [];
};
updateSplashes(cordovaProject, platformResourcesDir);
// The emit was called
expect(emitSpy).toHaveBeenCalled();
// 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);
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);
});
});
it('Test#02 : Should update splash png icon.', function () {
const updateSplashes = prepare.__get__('updateSplashes');
it('doesn\'t move main activity class java file when package name not changed', async () => {
packageName = 'com.company.product';
// 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('Test#03 : Should update splash webp icon.', function () {
const updateSplashes = prepare.__get__('updateSplashes');
// 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);
await api.prepare(cordovaProject, options).then(() => {
expect(replaceFileContents).toHaveBeenCalledTimes(0);
expect(ensureDirSyncSpy).toHaveBeenCalledTimes(0);
expect(copySyncSpy).toHaveBeenCalledTimes(0);
expect(removeSyncSpy).toHaveBeenCalledTimes(0);
});
});
});
});

View File

@@ -19,7 +19,7 @@
under the License.
*/
var android_sdk = require('cordova-android/lib/android_sdk');
const 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.
*/
var emulators = require('cordova-android/lib/emulator');
const 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,6 +20,7 @@
package __ID__;
import android.os.Bundle;
import org.apache.cordova.*;
public class __ACTIVITY__ extends CordovaActivity

View File

@@ -35,9 +35,10 @@
<activity android:name="__ACTIVITY__"
android:label="@string/activity_name"
android:launchMode="singleTop"
android:theme="@style/Theme.AppCompat.NoActionBar"
android:theme="@style/Theme.App.SplashScreen"
android:windowSoftInputMode="adjustResize"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode">
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
android:exported="true">
<intent-filter android:label="@string/launcher_name">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

View File

@@ -46,7 +46,7 @@ buildscript {
if(cordovaConfig.IS_GRADLE_PLUGIN_GOOGLE_SERVICES_ENABLED) {
// Checks if the kotlin version format is valid.
if(!cdvHelpers.isVersionValid(gradlePluginGoogleServicesVersion)) {
if(!cdvHelpers.isVersionValid(cordovaConfig.GRADLE_PLUGIN_GOOGLE_SERVICES_VERSION)) {
throw new GradleException("The defined Google Services plugin version (${cordovaConfig.GRADLE_PLUGIN_GOOGLE_SERVICES_VERSION}) does not appear to be a valid version.")
}
@@ -60,7 +60,13 @@ buildscript {
// Allow plugins to declare Maven dependencies via build-extras.gradle.
allprojects {
apply from: 'repositories.gradle'
def hasRepositoriesGradle = file('repositories.gradle').exists()
if (hasRepositoriesGradle) {
apply from: 'repositories.gradle'
} else {
apply from: "${project.rootDir}/repositories.gradle"
}
repositories repos
}
@@ -182,14 +188,14 @@ android {
maxSdkVersion cordovaConfig.MAX_SDK_VERSION
}
targetSdkVersion cordovaConfig.SDK_VERSION
compileSdkVersion cordovaConfig.SDK_VERSION
compileSdkVersion cordovaConfig.COMPILE_SDK_VERSION
}
lintOptions {
abortOnError false
}
buildToolsVersion cordovaConfig.LATEST_INSTALLED_BUILD_TOOLS
buildToolsVersion cordovaConfig.BUILD_TOOLS_VERSION
// This code exists for Crosswalk and other Native APIs.
// By default, we multiply the existing version code in the
@@ -281,6 +287,7 @@ 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,10 +27,18 @@ buildscript {
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
cdvHelpers.verifyCordovaConfigForBuild()
}
allprojects {
apply from: 'repositories.gradle'
def hasRepositoriesGradle = file('repositories.gradle').exists()
if (hasRepositoriesGradle) {
apply from: 'repositories.gradle'
} else {
apply from: "${project.rootDir}/repositories.gradle"
}
repositories repos
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

View File

@@ -0,0 +1,34 @@
<!--
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

@@ -0,0 +1,22 @@
<?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,4 +1,22 @@
<?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

@@ -0,0 +1,34 @@
<?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,7 +49,6 @@
<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,7 +21,7 @@ apply plugin: 'com.android.application'
apply from: '../../../framework/cordova.gradle'
android {
compileSdkVersion cordovaConfig.SDK_VERSION
compileSdkVersion cordovaConfig.COMPILE_SDK_VERSION
buildToolsVersion cordovaConfig.BUILD_TOOLS_VERSION
defaultConfig {

View File

@@ -29,7 +29,8 @@
<activity android:name=".EmbeddedWebViewActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name"
android:windowSoftInputMode="adjustPan">
android:windowSoftInputMode="adjustPan"
android:exported="true">
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -38,7 +39,8 @@
<activity android:name=".StandardActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name"
android:windowSoftInputMode="adjustPan">
android:windowSoftInputMode="adjustPan"
android:exported="true">
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -47,7 +49,8 @@
<activity android:name=".TestActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name"
android:windowSoftInputMode="adjustPan">
android:windowSoftInputMode="adjustPan"
android:exported="true">
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />