Compare commits

...

73 Commits
8.1.x ... 9.0.0

Author SHA1 Message Date
Erisu
cdcb4ddcd9 Set VERSION to 9.0.0 (via coho) 2020-06-23 18:33:21 +09:00
Erisu
8e53e2aa56 Update JS snapshot to version 9.0.0 (via coho) 2020-06-23 18:33:21 +09:00
Erisu
5276f56cc4 android-v9.0.0 Updated version and RELEASENOTES.md for release 9.0.0 2020-06-23 18:31:47 +09:00
エリス
9df6793f34 chore: set AndroidX off by default (#1005) 2020-06-23 16:13:04 +09:00
Duy Mac Van
d90e191837 Accept multiple mime types on file input (#971) 2020-06-18 09:59:44 -03:00
Norman Breau
d5d448888d fix: support both adaptive and standard icons at the same time (#1001) 2020-06-18 09:58:42 -03:00
Norman Breau
8d8600b442 fix: Plugin install fails when preview sdk is installed (#985) 2020-06-18 09:58:14 -03:00
Chris Brody
0bf6455153 cleanup yaml files (#994)
* remove trailing spaces from .asf.yaml
* remove trailing spaces from .eslintrc.yml
* add newline to end of .eslintrc.yml
2020-06-16 19:34:10 -04:00
Chris Brody
80b7a7f6ac cleanup: remove trailing spaces from Java sources (#999)
* remove trailing spaces from framework/src/org/apache/cordova/*.java
* remove trailing spaces from framework/src/org/apache/cordova/engine/*.java
2020-06-16 19:26:48 -04:00
Chris Brody
6b789c57e8 update some dependencies (#992)
* update cordova-common -> ^4.0.1
* update execa -> ^4.0.2
* update fs-extra -> ^9.0.1
2020-06-10 11:47:17 -04:00
Chris Brody
b8a344fc17 cleanup: remove trailing spaces from framework build files (#998)
* remove trailing spaces from framework/build.xml
* remove trailing spaces from framework/cordova.gradle
2020-06-10 09:32:43 -04:00
Chris Brody
1b6319e9cf cleanup: remove trailing spaces from project template (#997)
* remove trailing spaces from bin/templates/project/AndroidManifest.xml
* remove trailing spaces from bin/templates/project/assets/www/css/index.css
2020-06-10 09:31:51 -04:00
Chris Brody
2cc81253ff cleanup: remove trailing spaces from bat files (#996)
* remove trailing spaces from bin/*.bat
* remove trailing spaces from bin/templates/cordova/lib/*.bat
2020-06-10 09:30:51 -04:00
Chris Brody
499c694146 remove trailing spaces from markdown files (#995)
* remove trailing spaces from .github/ISSUE_TEMPLATE.md
* remove trailing spaces from .github/ISSUE_TEMPLATE/BUG_REPORT.md
* remove trailing spaces from .github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
* remove trailing spaces from CONTRIBUTING.md
2020-06-10 09:29:14 -04:00
Chris Brody
bc32cca281 update devDependencies (#993)
* update jasmine-spec-reporter -> ^5.0.2 in devDependencies

* update nyc -> ^15.1.0 in devDependencies
2020-06-10 09:27:11 -04:00
Chris Brody
dead4b4ab6 breaking: reduce combined response cutoff to 16 MB (#987)
* breaking: reduce combined response cutoff to 16 MB
* rename `MAX_PAYLOAD_SIZE` to `COMBINED_RESPONSE_CUTOFF`
* update some comments
* cleanup: split `if` statements into multiple lines
2020-06-09 19:48:33 -04:00
Chris Brody
305cb2cdd5 major: Gradle 6.5 & Android Gradle plugin 4.0.0 updates (#988)
* feat: Gradle 6.5 & Android Gradle plugin 4.0.0 updates

* update test/android/wrapper.gradle to reference Gradle 6.5

* update test/androidx/wrapper.gradle to reference Gradle 6.5

* update spec/fixtures/android_studio_project/build.gradle
  to reference com.android.tools.build:gradle:4.0.0

* update test/android/build.gradle
  to reference com.android.tools.build:gradle:4.0.0

* update test/androidx/build.gradle
  to reference com.android.tools.build:gradle:4.0.0
2020-06-08 20:47:10 -04:00
Chris Brody
287bfcbaa3 cleanup: remove trailing spaces from app/build.gradle (#990)
(in the template)
2020-06-08 12:42:38 -04:00
Chris Brody
2a61b751ab breaking: remove legacy/build.gradle from template (#989) 2020-06-08 10:42:15 -04:00
Norman Breau
a830145f36 fix: wait_for_boot waiting forever (#978) 2020-05-22 15:07:18 -03:00
GimpArm
08dc1dd9b9 Increased detectArchitecture() timeout (#965)
The timeout for detechArchitecture() is sometimes too low when devices are on wifi network connections and even sometimes over USB. The command can take up to 3 seconds to execute and return. Currently the timeout is set to 1000 ms and setting it to 5000 ms seems to be a good compromise.

Co-authored-by: Scott Downing <Scott.Downing@marc-cain.de>
2020-05-22 15:06:11 -03:00
Norman Breau
e86b211cd1 breaking: Bump android gradle plugin to 3.6.0 (#962) 2020-04-25 14:34:15 -03:00
Erisu
6c5febc189 chore(asf): update git notification settings 2020-04-22 13:29:31 +09:00
Norman Breau
4d0d60c294 Feature: JVM Args flag (#948)
* feat: JVM args flag

* test: JVM args flag

* feat: Do not display recommended memory warning unless if memory is less than cordova default
2020-04-16 09:39:22 -03:00
Norman Breau
16a88ec631 fix: ANDROID_SDK_ROOT variable (#951)
This commit does the following:
- Makes ANDROID_SDK_ROOT the primary variable to look for the Android SDK location.
- Makes ANDROID_HOME the fallback variable, if ANDROID_SDK_ROOT is not present/valid.

Gradle updates:
Note that the following gradle updates were required, otherwise the android gradle plugin did not honour the ANDROID_SDK_ROOT variable.

- Updates the framework's android studio's gradle plugin from version 3.3.0 to 3.5.3.
	Not only this is required for android's gradle to obey ANDROID_SDK_ROOT, it is now in sync with the Android test project/
- Updates the Androidx test project to use gralde plugin from version 3.3.0 to 3.5.3, to match Android Test & framework.
	- Consequentially, this required to also upgrade AndroidX test project to use Gradle 6.1, which also matches both the Android test project & framework

These changes above fixes #949

Additionally, since we update the environment variables dynamically, the environment variable printout produced misleading information.
The environment variable printout will now print out the variable as defined by the user (before the tooling messes with them). An additional log
is printed that tells the user exactly what Cordova is going to use for the Android SDK path. This should fix #670
2020-04-15 23:43:17 -03:00
Norman Breau
1ce2b6b315 test: synced androidx gradle versions to the same version as the android test (#959) 2020-04-15 23:42:33 -03:00
Norman Breau
43a6805902 feat: com.android.tools.build:gradle:3.5.3 (#960) 2020-04-15 23:42:15 -03:00
エリス
566262c923 chore(npm): add package-lock.json (#956) 2020-04-15 12:46:23 +09:00
エリス
e4bff281b2 chore(npm): add ignore list (#958) 2020-04-15 12:41:24 +09:00
エリス
9a675912f8 chore: various cleanup (#957)
* chore: various cleanup
  * update LICENSE year
  * update third-party appended licenses
  * update NOTICE year & format
  * use correct CI badges on README.md
  * remove extra markdown from RELEASENOTES.md
* chore: update reviewboard url
2020-04-15 12:40:59 +09:00
エリス
f86044e6ce chore(eslint): bump package & apply eslint fix (#955) 2020-04-15 12:36:40 +09:00
エリス
43fdaa91a3 breaking(npm): bump packages (#954)
* breaking(npm): bump dev dependencies
  * jasmine-spec-reporter@^5.0.1
  * nyc@^15.0.1
  * rewire@^5.0.0

* breaking(npm): bump dependencies
  * android-versions@^1.5.0
  * cordova-common@^4.0.0
  * execa@^4.0.0
  * fs-extra@^9.0.0
  * nopt@^4.0.3
  * which@^2.0.2
2020-04-15 12:20:28 +09:00
エリス
80f46aefcd chore(npm): use short notation in package.json (#953) 2020-04-15 11:53:22 +09:00
João Gonçalves
71f63d7b33 Prevent exit fullscreen mode from closing application (#823)
* (android) wrap custom view in FrameLayout

Wraps the custom view in a FrameLayout in order
to capture key events and redirect them to SystemWebView's
dispatchKeyEvent.

* Update framework/src/org/apache/cordova/CordovaWebViewImpl.java

Co-Authored-By: エリス <erisu@users.noreply.github.com>

* Update framework/src/org/apache/cordova/CordovaWebViewImpl.java

Co-Authored-By: エリス <erisu@users.noreply.github.com>

* Update framework/src/org/apache/cordova/CordovaWebViewImpl.java

Co-Authored-By: エリス <erisu@users.noreply.github.com>

* remove empty line below @override

Co-authored-by: Norman Breau <norman@normanbreau.com>
Co-authored-by: エリス <erisu@users.noreply.github.com>
2020-04-14 18:48:06 +00:00
Niklas Merz
8d47cd73c0 Update CONTRIBUTING.md 2020-04-13 13:56:17 +02:00
Norman Breau
6d451bc6f9 fix: Removed redundent logcat print (#950)
The chromium webview will print an informational log already,
we don't need to override the method to do what the webview will already.
2020-04-11 23:25:20 -03:00
Bas Bosman
6402e7b755 Bump minSdkVersion to 22 and drop pre-Lollipop specific code (#915)
* feat: bump minSdkVersion to 22

BREAKING CHANGE: drop KitKat support

* chore: remove obsolete comment

* feat: remove pre-Lollipop specific code

* chore: remove KitKat from needsKitKatContentUrlFix

* chore: other minor cleanup
2020-04-03 19:54:08 +02:00
Norman Breau
c93f93f637 fix: GH-873 App bundle builds to obey command-line arguments (#941) 2020-04-01 01:59:39 -03:00
エリス
6e51943d15 ci: drop travis & move codecov to gh-actions (#940) 2020-04-01 13:35:07 +09:00
Norman Breau
c81cd871f8 Updated README to reflect what Android requires more accurately, which is Java 8, not anything less, not anything greater. Java 1.8.x is required. (#929) 2020-04-01 00:55:47 -03:00
Norman Breau
8ab1dbc373 fix: GH-935 replaced compare-func with native sort method (#937) 2020-04-01 00:43:36 -03:00
エリス
fb26050fab fix: test failure with shebang interpreter in rewired files (#939)
* breaking: remove shebang from create
* breaking: remove shebang from version
* breaking: remove shebang from logical files
2020-04-01 12:04:35 +09:00
Sukant Pal
c56cd4d5a8 refactor: use es6 class (#911)
Refactored to Classes:
* Api
* AndroidManifest
* AndroidProject
2020-02-01 17:34:51 +09:00
エリス
de105e8651 refactor (eslint): use cordova-eslint (#910) 2020-01-31 22:02:48 +09:00
エリス
8e98de6e7c chore: remove appveyor residual (#909) 2020-01-29 19:28:07 +09:00
エリス
6372ca3fac feat: add github actions (#895) 2020-01-29 14:29:05 +09:00
Norman Breau
3712619f5c refactor: remove shelljs dependency (#842)
Co-authored-by: エリス <erisu@users.noreply.github.com>
2020-01-29 10:12:55 +09:00
エリス
dee1e77d0b feat: add kotlin support (#896)
Co-authored-by: Joshua Chandler <joshchandler88@gmail.com>
2020-01-27 16:14:58 +09:00
エリス
d01ed80a61 feat: add androidx support (#901) 2020-01-27 15:52:08 +09:00
Norman Breau
92268b2e76 fix: cordova requirements consider the android-targetSdkVersion (#849)
* Made cordova requirements consider the android-targetSdkVersion preference
* refator: get_target method
Added comments.
Added JSDoc block
Reduced error exit point to one spot

Co-authored-by: エリス <erisu@users.noreply.github.com>
2020-01-24 10:53:49 +09:00
エリス
0924654a47 fix (adb): shell to return expected stdout (#904) 2020-01-21 23:37:22 +09:00
Norman Breau
8ef742e79d feat: upgrade gradle to 6.1 & gradle build tools to 3.5.3 (#792)
* feat!: upgrade gradle to 6.1
* feat!: upgrade gradle build tools to 3.5.3
* feat: added `npm run clean-tests`
* fix!: Removed useDeprecatedNdk as this option is now completely removed from gradle.
* feat!: bump gradle to 6.1 & gradle build tools to 3.5.3

Co-authored-by: エリス <erisu@users.noreply.github.com>
2020-01-21 19:24:00 +09:00
エリス
64ef13c6e0 chore: remove .project file & add .settings to gitignore (#902) 2020-01-20 11:41:56 +09:00
Raphael von der Grün
66ad2c948e refactor: simplify doFindLatestInstalledBuildTools (#900)
* chore: update com.g00fy2:versioncompare to 1.3.4

* refactor: flatten error handling in doFindLatestInstalledBuildTools

* refactor: inline & simplify getAvailableBuildTools

* refactor: use string interpolation for error messages
2020-01-17 13:43:25 +01:00
Kristian Heljas
60e022fedd feat: use java package name for loading BuildConfig (#751) 2020-01-17 20:46:45 +09:00
エリス
09256b766f chore: rename gradle plugin google services preference options (#898) 2020-01-17 08:22:06 +09:00
エリス
a951793431 feat: add google services support (#893)
* feat: Add support for GoogleServicesEnabled preference option
* fix: wrap google-services classpath with GoogleServicesEnabled flag
* chore: bump google-services version to 4.2.0
* feat: Add support for GoogleServicesVersion preference option

Co-authored-by: Maksim Chemerisuk <chemerisuk@users.noreply.github.com>
2020-01-16 08:40:54 +09:00
Ramazan VAPURCU
f4b8f44d4a feat: add version-compare library to compare build-tools versions properly. (#709)
Closes #708
2020-01-15 07:28:54 +09:00
Norman Breau
08ab7d4b59 Ignore auto-generated eclipse buildship files (#831)
* git ignore eclipse build ship files that gets auto-generated in Eclipse and some other code editors.

* Changed **./project to .project as suggested

Co-Authored-By: Raphael von der Grün <raphinesse@gmail.com>

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>
2020-01-07 15:32:19 -04:00
Norman Breau
5889001465 Increased default target sdk to 29 (#848)
* (android) increased default target sdk to 29

* Updated travis and appveyor to use build tools/api level 29

* Fixed two other files that was missing the API 29 update
2020-01-07 11:47:27 -04:00
Norman Breau
91d2716122 [Major] Removed unnecessary project name restriction (#859)
* removed unnecessary restriction that prevented project names from starting with a number. Project names starting with a number is perfectly valid.

* Reworded validateProjectName jsdoc

Co-Authored-By: Raphael von der Grün <raphinesse@gmail.com>

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>
2020-01-07 10:10:04 -04:00
エリス
0e6ad28e56 chore: drop q module (#833)
* chore: drop q module
* chore: fix & complete dropping q
* Fix faulty transformation of Q.when
* Simplify thenResolve transformation
  * Removes unnecesary Promise wrapping in onFulfilled callback.
* Transform .done calls to .then or .catch
  * The important thing is that we always handle rejections.
* Remove Q from specs
Requires Jasmine 3.5
* Replace Q.timeout w/ Promise.race & custom function

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>
2020-01-07 21:22:59 +09:00
Raphael von der Grün
fd57909730 chore: replace superspawn & child_process with execa (#862)
* chore: added execa dependency

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>

* chore: execa - drop superspawn in android_sdk

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>

* chore: execa - drop superspawn in build

* chore: execa - drop superspawn in check_reqs

Plus: Remove useless trimming of execa output

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>

* chore: execa - drop superspawn in emulator

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>

* chore: execa - drop superspawn in device

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>

* chore: execa - drop superspawn in run_java_unit_tests

* chore: execa - drop superspawn in ProjectBuilder

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>

* chore: execa - drop superspawn in adb

* chore: execa - drop superspawn in plugin.spec

* chore: execa - replace child_process in log

* chore: execa - replace child_process in check_reqs

* chore: execa - replace child_process in emulator

Co-authored-by: エリス <erisu@users.noreply.github.com>
2020-01-06 23:15:22 +01:00
Raphael von der Grün
e3cc75caff feat: don't filter gradle's stderr anymore (#860) 2020-01-06 12:15:25 +09:00
エリス
e26142f43b chore: drop node 6 and 8 support (#832) 2020-01-06 12:10:03 +09:00
エリス
d0f972e128 chore: bump version to 9.0.0-dev (#890) 2020-01-06 12:07:05 +09:00
任跃兵
e42fedc820 Optimization code (#697)
This judgment condition has no use.
2019-11-14 19:24:10 +00:00
Adri Van Houdt
f0c9814c04 chore: removed comment that serves no purpose (#863) 2019-11-04 15:42:49 +00:00
Raphael von der Grün
529278190c Update jasmine to 3.5.0 (#861) 2019-11-03 21:44:16 +01:00
Raphael von der Grün
fcaab36484 Modernize our one E2E test (#858)
The test should still do the same thing as before.
No change tested code intended.

This also gets rid of the barely used and outdated E2E helpers
2019-10-30 16:24:32 +01:00
Raphael von der Grün
5dfa995a4b Ensure to lint as many files as possible (#854)
* Lint everything, including bins w/out extension

* Apply eslint --fix to all linted files

* Manually fix all remaining lint rule violations

* Remove ESLint inline config
2019-10-21 18:26:17 +02:00
Erisu
c35a990c09 Update JS snapshot to version 8.2.0-dev (via coho) 2019-09-11 13:58:08 +09:00
Erisu
11f40bd2cc Set VERSION to 8.2.0-dev (via coho) 2019-09-11 13:58:04 +09:00
213 changed files with 11049 additions and 2819 deletions

22
.asf.yaml Normal file
View File

@@ -0,0 +1,22 @@
# 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.
notifications:
commits: commits@cordova.apache.org
issues: issues@cordova.apache.org
pullrequests_status: issues@cordova.apache.org
pullrequests_comment: issues@cordova.apache.org

View File

@@ -1,2 +1,3 @@
bin/templates/project/assets/www/cordova.js
test/app
test/android/app
test/androidx/app

View File

@@ -1,10 +1,27 @@
root: true
extends: semistandard
rules:
indent:
- error
- 4
camelcase: off
padded-blocks: off
operator-linebreak: off
no-throw-literal: off
# 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.
extends: '@cordova/eslint-config/node'
overrides:
- files: [spec/**/*.js]
extends: '@cordova/eslint-config/node-tests'
rules:
prefer-promise-reject-errors: off
- files: [cordova-js-src/**/*.js]
extends: '@cordova/eslint-config/browser'

View File

@@ -24,10 +24,10 @@ We very much prefer issues created by using one of these templates.
### Version information
<!--
<!--
What are relevant versions you are using?
For example:
Cordova: Cordova CLI, Cordova Platforms, Cordova Plugins
Cordova: Cordova CLI, Cordova Platforms, Cordova Plugins
Other Frameworks: Ionic Framework and CLI version
Operating System, Android Studio, Xcode etc.
-->

View File

@@ -32,10 +32,10 @@ about: If something isn't working as expected.
### Version information
<!--
<!--
What are relevant versions you are using?
For example:
Cordova: Cordova CLI, Cordova Platforms, Cordova Plugins
Cordova: Cordova CLI, Cordova Platforms, Cordova Plugins
Other Frameworks: Ionic Framework and CLI version
Operating System, Android Studio, Xcode etc.
-->

View File

@@ -12,17 +12,17 @@ about: A suggestion for a new functionality
## Feature Description
<!--
<!--
Describe your feature request in detail
Please provide any code examples or screenshots of what this feature would look like
Are there any drawbacks? Will this break anything for existing users?
Are there any drawbacks? Will this break anything for existing users?
-->
## Alternatives or Workarounds
<!--
Describe alternatives or workarounds you are currently using
<!--
Describe alternatives or workarounds you are currently using
Are there ways to do this with existing functionality?
-->

61
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,61 @@
# 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.
name: Node CI
on: [push, pull_request]
jobs:
test:
name: NodeJS ${{ matrix.node-version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
node-version: [10.x, 12.x]
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Environment Information
run: |
node --version
npm --version
gradle --version
- name: npm install and test
run: |
npm i
npm t
env:
CI: true
- uses: codecov/codecov-action@v1
with:
fail_ci_if_error: true

16
.gitignore vendored
View File

@@ -26,9 +26,14 @@ example
/framework/javadoc-public
/framework/javadoc-private
/test/.externalNativeBuild
/test/gradle
/test/gradlew
/test/gradlew.bat
/test/android/gradle
/test/android/gradlew
/test/android/gradlew.bat
/test/androidx/gradle
/test/androidx/gradlew
/test/androidx/gradlew.bat
/test/assets/www/.tmp*
/test/assets/www/cordova.js
/test/bin
@@ -45,4 +50,7 @@ npm-debug.log
node_modules/
coverage/
.nyc_output/
package-lock.json
# Eclipse Buildship files
.project
.settings
.classpath

5
.npmignore Normal file
View File

@@ -0,0 +1,5 @@
.*
coverage
test
spec
framework/build

View File

@@ -3,7 +3,6 @@ bin
gen
proguard-project.txt
spec
appveyor.yml
framework/build
ic_launcher.png
build

View File

@@ -4,5 +4,5 @@
GUESS_FIELDS = True
OPEN_BROWSER = True
TARGET_GROUPS = 'cordova'
REVIEWBOARD_URL = 'http://reviews.apache.org'
REVIEWBOARD_URL = 'https://reviews.apache.org'

View File

@@ -1,48 +0,0 @@
sudo: false
# NOTE: dist is added here to ensure that Travis CI works consistently
# on Node.js 12. It is desired to remove it once this issue is resolved
# or otherwise goes away.
dist: trusty
env:
global:
- ANDROID_API_LEVEL=28
- ANDROID_BUILD_TOOLS_VERSION=28.0.3
- TERM=dumb # Keep gradle from crapping all over the log
matrix:
- nodejs_version=6
- nodejs_version=8
- nodejs_version=10
- nodejs_version=12
language: android
jdk:
- oraclejdk8
android:
components:
- tools
- build-tools-$ANDROID_BUILD_TOOLS_VERSION
- android-$ANDROID_API_LEVEL
licenses:
- 'android-sdk-preview-license-.+'
- 'android-sdk-license-.+'
- 'google-gdk-license-.+'
before_install:
- nvm install $nodejs_version
- node --version
- npm --version
- gradle --version
install:
- npm install
- npm install -g codecov
script:
- npm test
- npm run cover
after_script:
- codecov

View File

@@ -25,12 +25,12 @@ Anyone can contribute to Cordova. And we need your contributions.
There are multiple ways to contribute: report bugs, improve the docs, and
contribute code.
For instructions on this, start with the
[contribution overview](http://cordova.apache.org/contribute/).
The details are explained there, but the important items are:
- Have a Jira issue open that corresponds to your contribution.
- Check for Github issues that corresponds to your contribution and link or create them if necessary.
- Run the tests so your patch doesn't break existing functionality.
We look forward to your contributions!

101
LICENSE
View File

@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2015 Apache Cordova
Copyright 2015-2020 Apache Cordova
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -207,7 +207,7 @@
bin/node_modules/q
================================================================================
Copyright 20092012 Kristopher Michael Kowal. All rights reserved.
Copyright 20092017 Kristopher Michael Kowal. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
@@ -226,89 +226,42 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
================================================================================
bin/node_modules/shelljs
================================================================================
Copyright (c) 2012, Artur Adib <aadib@mozilla.com>
All rights reserved.
You may use this project under the terms of the New BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Artur Adib nor the
names of the contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL ARTUR ADIB BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================================================
bin/node_modules/nopt
================================================================================
Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
All rights reserved.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The ISC License
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright (c) Isaac Z. Schlueter and Contributors
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
================================================================================
bin/node_modules/which
================================================================================
Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
All rights reserved.
The ISC License
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

14
NOTICE
View File

@@ -1,15 +1,5 @@
Apache Cordova
Copyright 2015 The Apache Software Foundation
Copyright 2015-2020 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org)
=========================================================================
== NOTICE file corresponding to the section 4 d of ==
== the Apache License, Version 2.0, ==
== in this case for the Android-specific code. ==
=========================================================================
This product includes software developed as part of
The Android Open Source Project (http://source.android.com).
The Apache Software Foundation (http://www.apache.org/).

View File

@@ -19,23 +19,21 @@
#
-->
[![Build status](https://ci.appveyor.com/api/projects/status/github/apache/cordova-android?branch=master)](https://ci.appveyor.com/project/Humbedooh/cordova-android)
[![Build Status](https://travis-ci.org/apache/cordova-android.svg?branch=master)](https://travis-ci.org/apache/cordova-android)
[![codecov.io](https://codecov.io/github/apache/cordova-android/coverage.svg?branch=master)](https://codecov.io/github/apache/cordova-android?branch=master)
# Cordova Android
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.
[![NPM](https://nodei.co/npm/cordova-android.png)](https://nodei.co/npm/cordova-android/)
[![Node CI](https://github.com/apache/cordova-android/workflows/Node%20CI/badge.svg?branch=master)](https://github.com/apache/cordova-android/actions?query=branch%3Amaster)
[![codecov.io](https://codecov.io/github/apache/cordova-android/coverage.svg?branch=master)](https://codecov.io/github/apache/cordova-android?branch=master)
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).
## Requires
- Java JDK 1.8 or greater
- Android SDK [http://developer.android.com](http://developer.android.com)
- Java JDK 1.8
- Android SDK [http://developer.android.com](https://developer.android.com/)
## Cordova Android Developer Tools
@@ -62,6 +60,4 @@ These commands live in a generated Cordova Android project. Any interactions wit
## Running the Native Tests
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!
The `test/` directory in this project contains an Android test project that can be used to run different kinds of native tests. Check out the [README contained therein](test/README.md) for more details!

View File

@@ -18,7 +18,76 @@
# under the License.
#
-->
## Release Notes for Cordova (Android) ##
## Release Notes for Cordova (Android)
### 9.0.0 (Jun 23, 2020)
* [GH-1005](https://github.com/apache/cordova-android/pull/1005) chore: set AndroidX off by default
* [GH-971](https://github.com/apache/cordova-android/pull/971) fix: Accept multiple mime types on file input
* [GH-1001](https://github.com/apache/cordova-android/pull/1001) fix: support both adaptive and standard icons at the same time
* [GH-985](https://github.com/apache/cordova-android/pull/985) fix: Plugin install fails when preview sdk is installed
* [GH-994](https://github.com/apache/cordova-android/pull/994) chore: cleanup yaml files
* [GH-999](https://github.com/apache/cordova-android/pull/999) chore: remove trailing spaces from Java sources
* [GH-992](https://github.com/apache/cordova-android/pull/992) chore: update some dependencies
* [GH-998](https://github.com/apache/cordova-android/pull/998) chore: remove trailing spaces from framework build files
* [GH-997](https://github.com/apache/cordova-android/pull/997) chore: remove trailing spaces from project template
* [GH-996](https://github.com/apache/cordova-android/pull/996) chore: remove trailing spaces from bat files
* [GH-995](https://github.com/apache/cordova-android/pull/995) remove trailing spaces from markdown files
* [GH-993](https://github.com/apache/cordova-android/pull/993) chore: update `devDependencies`
* [GH-987](https://github.com/apache/cordova-android/pull/987) breaking: reduce combined response cutoff to 16 MB
* [GH-988](https://github.com/apache/cordova-android/pull/988) major: Gradle 6.5 & **Android** Gradle plugin 4.0.0 updates
* [GH-990](https://github.com/apache/cordova-android/pull/990) chore: remove trailing spaces from `app/build.gradle`
* [GH-989](https://github.com/apache/cordova-android/pull/989) breaking: remove `legacy/build.gradle` from template
* [GH-978](https://github.com/apache/cordova-android/pull/978) fix: `wait_for_boot` waiting forever
* [GH-965](https://github.com/apache/cordova-android/pull/965) fix: Increased `detectArchitecture()` timeout
* [GH-962](https://github.com/apache/cordova-android/pull/962) breaking: Bump **Android** gradle plugin to 3.6.0
* [GH-948](https://github.com/apache/cordova-android/pull/948) feature: JVM Args flag
* [GH-951](https://github.com/apache/cordova-android/pull/951) fix: `ANDROID_SDK_ROOT` variable
* [GH-959](https://github.com/apache/cordova-android/pull/959) test: synced AndroidX gradle versions to the same version as the **Android** test
* [GH-960](https://github.com/apache/cordova-android/pull/960) feat: `com.android.tools.build:gradle:3.5.3`
* [GH-956](https://github.com/apache/cordova-android/pull/956) chore(npm): add `package-lock.json`
* [GH-958](https://github.com/apache/cordova-android/pull/958) chore(npm): add ignore list
* [GH-957](https://github.com/apache/cordova-android/pull/957) chore: various cleanup
* [GH-955](https://github.com/apache/cordova-android/pull/955) chore(eslint): bump package & apply eslint fix
* [GH-954](https://github.com/apache/cordova-android/pull/954) breaking(npm): bump packages
* [GH-953](https://github.com/apache/cordova-android/pull/953) chore(npm): use short notation in `package.json`
* [GH-823](https://github.com/apache/cordova-android/pull/823) fix: prevent exit fullscreen mode from closing application
* [GH-950](https://github.com/apache/cordova-android/pull/950) fix: removed redundent logcat print
* [GH-915](https://github.com/apache/cordova-android/pull/915) breaking: bump minSdkVersion to 22 and drop pre-Lollipop specific code
* [GH-941](https://github.com/apache/cordova-android/pull/941) fix: GH-873 App bundle builds to obey command-line arguments
* [GH-940](https://github.com/apache/cordova-android/pull/940) ci: drop travis & move codecov to gh-actions
* [GH-929](https://github.com/apache/cordova-android/pull/929) chore: updated `README` to reflect what **Android** requires more accurately, which is Java 8, not anything less, not anything greater. Java 1.8.x is required.
* [GH-937](https://github.com/apache/cordova-android/pull/937) fix: GH-935 replaced `compare-func` with native sort method
* [GH-939](https://github.com/apache/cordova-android/pull/939) fix: test failure with shebang interpreter in `rewired` files
* [GH-911](https://github.com/apache/cordova-android/pull/911) refactor: use es6 class
* [GH-910](https://github.com/apache/cordova-android/pull/910) refactor (eslint): use `cordova-eslint`
* [GH-909](https://github.com/apache/cordova-android/pull/909) chore: remove appveyor residual
* [GH-895](https://github.com/apache/cordova-android/pull/895) feat: add github actions
* [GH-842](https://github.com/apache/cordova-android/pull/842) refactor: remove `shelljs` dependency
* [GH-896](https://github.com/apache/cordova-android/pull/896) feat: add Kotlin support
* [GH-901](https://github.com/apache/cordova-android/pull/901) feat: add AndroidX support
* [GH-849](https://github.com/apache/cordova-android/pull/849) fix: cordova requirements consider the `android-targetSdkVersion`
* [GH-904](https://github.com/apache/cordova-android/pull/904) fix (adb): shell to return expected stdout
* [GH-792](https://github.com/apache/cordova-android/pull/792) feat: upgrade `gradle` to 6.1 & gradle build tools to 3.5.3
* [GH-902](https://github.com/apache/cordova-android/pull/902) chore: remove `.project` file & add `.settings` to `gitignore`
* [GH-900](https://github.com/apache/cordova-android/pull/900) refactor: simplify `doFindLatestInstalledBuildTools`
* [GH-751](https://github.com/apache/cordova-android/pull/751) feat: use Java package name for loading `BuildConfig`
* [GH-898](https://github.com/apache/cordova-android/pull/898) chore: rename gradle plugin google services `preference` options
* [GH-893](https://github.com/apache/cordova-android/pull/893) feat: add Google Services support
* [GH-709](https://github.com/apache/cordova-android/pull/709) feat: add `version-compare` library to compare `build-tools` versions properly.
* [GH-831](https://github.com/apache/cordova-android/pull/831) chore: ignore auto-generated eclipse buildship files
* [GH-848](https://github.com/apache/cordova-android/pull/848) breaking: increased default target sdk to 29
* [GH-859](https://github.com/apache/cordova-android/pull/859) breaking: removed unnecessary project name restriction
* [GH-833](https://github.com/apache/cordova-android/pull/833) chore: drop `q` module
* [GH-862](https://github.com/apache/cordova-android/pull/862) chore: replace `superspawn` & `child_process` with `execa`
* [GH-860](https://github.com/apache/cordova-android/pull/860) feat: don't filter gradle's stderr anymore
* [GH-832](https://github.com/apache/cordova-android/pull/832) chore: drop node 6 and 8 support
* [GH-890](https://github.com/apache/cordova-android/pull/890) chore: bump version to 9.0.0-dev
* [GH-697](https://github.com/apache/cordova-android/pull/697) chore: optimization code
* [GH-863](https://github.com/apache/cordova-android/pull/863) chore: removed comment that serves no purpose
* [GH-861](https://github.com/apache/cordova-android/pull/861) chore: update `jasmine` to 3.5.0
* [GH-858](https://github.com/apache/cordova-android/pull/858) chore: modernize our one E2E test
* [GH-854](https://github.com/apache/cordova-android/pull/854) chore: ensure to lint as many files as possible
### 8.1.0 (Sep 11, 2019)

View File

@@ -1 +1 @@
8.1.0-dev
9.0.0

View File

@@ -1,37 +0,0 @@
environment:
ANDROID_HOME: "C:\\android"
# If the gradle daemon is used, the build hangs after generating the wrapper
GRADLE_OPTS: -Dorg.gradle.daemon=false
# URL for SDK Tools, Revision 26.1.1 (September 2017)
SDK_TOOLS_URL: https://dl.google.com/android/repository/sdk-tools-windows-4333796.zip
matrix:
- nodejs_version: 6
- nodejs_version: 8
- nodejs_version: 10
- nodejs_version: 12
install:
# Install Android SDK Tools
- mkdir "%ANDROID_HOME%"
- appveyor DownloadFile "%SDK_TOOLS_URL%" -FileName "%TMP%/sdk-tools.zip"
- 7z x "%TMP%/sdk-tools.zip" -o"%ANDROID_HOME%" > nul
- set PATH=%PATH%;"%ANDROID_HOME%\tools\bin"
- yes 2> nul | sdkmanager --licenses > nul
- sdkmanager "build-tools;28.0.3"
- choco install gradle --version 3.4.1
- ps: Install-Product node $env:nodejs_version
- npm install
build: off
test_script:
- gradle --version
- node --version
- npm --version
- npm test

View File

@@ -21,7 +21,7 @@
var android_sdk = require('./templates/cordova/lib/android_sdk');
android_sdk.print_newest_available_sdk_target().done(null, function (err) {
android_sdk.print_newest_available_sdk_target().catch(err => {
console.error(err);
process.exit(2);
});

View File

@@ -5,9 +5,9 @@
:: 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

View File

@@ -21,7 +21,7 @@
var check_reqs = require('./templates/cordova/lib/check_reqs');
check_reqs.run().done(
check_reqs.run().then(
function success () {
console.log('Looks like your environment fully supports cordova-android development!');
},

View File

@@ -5,9 +5,9 @@
:: 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

28
bin/clean_test.js Normal file
View File

@@ -0,0 +1,28 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
const fs = require('fs');
if (fs.existsSync('./test/gradlew')) {
fs.unlinkSync('./test/gradlew');
}
if (fs.existsSync('./test/gradlew.bat')) {
fs.unlinkSync('./test/gradlew.bat');
}

View File

@@ -23,12 +23,12 @@ var ConfigParser = require('cordova-common').ConfigParser;
var Api = require('./templates/cordova/Api');
var argv = require('nopt')({
'help': Boolean,
'cli': Boolean,
'shared': Boolean,
'link': Boolean,
help: Boolean,
cli: Boolean,
shared: Boolean,
link: Boolean,
'activity-name': [String, undefined]
}, { 'd': '--verbose' });
}, { d: '--verbose' });
if (argv.help || argv.argv.remain.length === 0) {
console.log('Usage: ' + path.relative(process.cwd(), path.join(__dirname, 'create')) + ' <path_to_new_project> <package_name> <project_name> [<template_path>] [--activity-name <activity_name>] [--link]');
@@ -55,4 +55,7 @@ var options = {
require('./templates/cordova/loggingHelper').adjustLoggerLevel(argv);
Api.createPlatform(argv.argv.remain[0], config, options).done();
Api.createPlatform(argv.argv.remain[0], config, options).catch(err => {
console.error(err);
process.exitCode = 1;
});

View File

@@ -5,9 +5,9 @@
:: 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
@@ -23,4 +23,4 @@ IF EXIST %script_path% (
ECHO.
ECHO ERROR: Could not find 'create' script in 'bin' folder, aborting...>&2
EXIT /B 1
)
)

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -19,10 +17,9 @@
under the License.
*/
var shell = require('shelljs');
var Q = require('q');
var path = require('path');
var fs = require('fs');
var fs = require('fs-extra');
var utils = require('../../bin/lib/utils');
var check_reqs = require('./../templates/cordova/lib/check_reqs');
var ROOT = path.join(__dirname, '..', '..');
@@ -34,20 +31,12 @@ var AndroidManifest = require('../templates/cordova/lib/AndroidManifest');
// (since we can then mock and control behaviour of all of these functions)
exports.validatePackageName = validatePackageName;
exports.validateProjectName = validateProjectName;
exports.setShellFatal = setShellFatal;
exports.copyJsAndLibrary = copyJsAndLibrary;
exports.copyScripts = copyScripts;
exports.copyBuildRules = copyBuildRules;
exports.writeProjectProperties = writeProjectProperties;
exports.prepBuildFiles = prepBuildFiles;
function setShellFatal (value, func) {
var oldVal = shell.config.fatal;
shell.config.fatal = value;
func();
shell.config.fatal = oldVal;
}
function getFrameworkDir (projectPath, shared) {
return shared ? path.join(ROOT, 'framework') : path.join(projectPath, 'CordovaLib');
}
@@ -56,53 +45,33 @@ function copyJsAndLibrary (projectPath, shared, projectName, isLegacy) {
var nestedCordovaLibPath = getFrameworkDir(projectPath, false);
var srcCordovaJsPath = path.join(ROOT, 'bin', 'templates', 'project', 'assets', 'www', 'cordova.js');
var app_path = path.join(projectPath, 'app', 'src', 'main');
const platform_www = path.join(projectPath, 'platform_www');
if (isLegacy) {
app_path = projectPath;
}
shell.cp('-f', srcCordovaJsPath, path.join(app_path, 'assets', 'www', 'cordova.js'));
fs.copySync(srcCordovaJsPath, path.join(app_path, 'assets', 'www', 'cordova.js'));
// Copy the cordova.js file to platforms/<platform>/platform_www/
// The www dir is nuked on each prepare so we keep cordova.js in platform_www
shell.mkdir('-p', path.join(projectPath, 'platform_www'));
shell.cp('-f', srcCordovaJsPath, path.join(projectPath, 'platform_www'));
fs.ensureDirSync(platform_www);
fs.copySync(srcCordovaJsPath, path.join(platform_www, 'cordova.js'));
// Copy cordova-js-src directory into platform_www directory.
// We need these files to build cordova.js if using browserify method.
shell.cp('-rf', path.join(ROOT, 'cordova-js-src'), path.join(projectPath, 'platform_www'));
fs.copySync(path.join(ROOT, 'cordova-js-src'), path.join(platform_www, 'cordova-js-src'));
// Don't fail if there are no old jars.
exports.setShellFatal(false, function () {
shell.ls(path.join(app_path, 'libs', 'cordova-*.jar')).forEach(function (oldJar) {
console.log('Deleting ' + oldJar);
shell.rm('-f', oldJar);
});
var wasSymlink = true;
try {
// Delete the symlink if it was one.
fs.unlinkSync(nestedCordovaLibPath);
} catch (e) {
wasSymlink = false;
}
// Delete old library project if it existed.
if (shared) {
shell.rm('-rf', nestedCordovaLibPath);
} else if (!wasSymlink) {
// Delete only the src, since Eclipse / Android Studio can't handle their project files being deleted.
shell.rm('-rf', path.join(nestedCordovaLibPath, 'src'));
}
});
if (shared) {
var relativeFrameworkPath = path.relative(projectPath, getFrameworkDir(projectPath, true));
fs.symlinkSync(relativeFrameworkPath, nestedCordovaLibPath, 'dir');
} else {
shell.mkdir('-p', nestedCordovaLibPath);
shell.cp('-f', path.join(ROOT, 'framework', 'AndroidManifest.xml'), nestedCordovaLibPath);
shell.cp('-f', path.join(ROOT, 'framework', 'project.properties'), nestedCordovaLibPath);
shell.cp('-f', path.join(ROOT, 'framework', 'build.gradle'), nestedCordovaLibPath);
shell.cp('-f', path.join(ROOT, 'framework', 'cordova.gradle'), nestedCordovaLibPath);
shell.cp('-r', path.join(ROOT, 'framework', 'src'), nestedCordovaLibPath);
fs.ensureDirSync(nestedCordovaLibPath);
fs.copySync(path.join(ROOT, 'framework', 'AndroidManifest.xml'), path.join(nestedCordovaLibPath, 'AndroidManifest.xml'));
fs.copySync(path.join(ROOT, 'framework', 'project.properties'), path.join(nestedCordovaLibPath, 'project.properties'));
fs.copySync(path.join(ROOT, 'framework', 'build.gradle'), path.join(nestedCordovaLibPath, 'build.gradle'));
fs.copySync(path.join(ROOT, 'framework', 'cordova.gradle'), path.join(nestedCordovaLibPath, 'cordova.gradle'));
fs.copySync(path.join(ROOT, 'framework', 'src'), path.join(nestedCordovaLibPath, 'src'));
}
}
@@ -151,12 +120,12 @@ function copyBuildRules (projectPath, isLegacy) {
if (isLegacy) {
// The project's build.gradle is identical to the earlier build.gradle, so it should still work
shell.cp('-f', path.join(srcDir, 'legacy', 'build.gradle'), projectPath);
shell.cp('-f', path.join(srcDir, 'wrapper.gradle'), projectPath);
fs.copySync(path.join(srcDir, 'legacy', 'build.gradle'), path.join(projectPath, 'legacy', 'build.gradle'));
fs.copySync(path.join(srcDir, 'wrapper.gradle'), path.join(projectPath, 'wrapper.gradle'));
} else {
shell.cp('-f', path.join(srcDir, 'build.gradle'), projectPath);
shell.cp('-f', path.join(srcDir, 'app', 'build.gradle'), path.join(projectPath, 'app'));
shell.cp('-f', path.join(srcDir, 'wrapper.gradle'), projectPath);
fs.copySync(path.join(srcDir, 'build.gradle'), path.join(projectPath, 'build.gradle'));
fs.copySync(path.join(srcDir, 'app', 'build.gradle'), path.join(projectPath, 'app', 'build.gradle'));
fs.copySync(path.join(srcDir, 'wrapper.gradle'), path.join(projectPath, 'wrapper.gradle'));
}
}
@@ -165,24 +134,29 @@ function copyScripts (projectPath) {
var srcScriptsDir = path.join(bin, 'templates', 'cordova');
var destScriptsDir = path.join(projectPath, 'cordova');
// Delete old scripts directory if this is an update.
shell.rm('-rf', destScriptsDir);
fs.removeSync(destScriptsDir);
// Copy in the new ones.
shell.cp('-r', srcScriptsDir, projectPath);
fs.copySync(srcScriptsDir, destScriptsDir);
let nodeModulesDir = path.join(ROOT, 'node_modules');
if (fs.existsSync(nodeModulesDir)) shell.cp('-r', nodeModulesDir, destScriptsDir);
const nodeModulesDir = path.join(ROOT, 'node_modules');
if (fs.existsSync(nodeModulesDir)) fs.copySync(nodeModulesDir, path.join(destScriptsDir, 'node_modules'));
fs.copySync(path.join(bin, 'check_reqs'), path.join(destScriptsDir, 'check_reqs'));
fs.copySync(path.join(bin, 'check_reqs.bat'), path.join(destScriptsDir, 'check_reqs.bat'));
fs.copySync(path.join(bin, 'android_sdk_version'), path.join(destScriptsDir, 'android_sdk_version'));
fs.copySync(path.join(bin, 'android_sdk_version.bat'), path.join(destScriptsDir, 'android_sdk_version.bat'));
shell.cp(path.join(bin, 'check_reqs*'), destScriptsDir);
shell.cp(path.join(bin, 'android_sdk_version*'), destScriptsDir);
var check_reqs = path.join(destScriptsDir, 'check_reqs');
var android_sdk_version = path.join(destScriptsDir, 'android_sdk_version');
// TODO: the two files being edited on-the-fly here are shared between
// platform and project-level commands. the below `sed` is updating the
// platform and project-level commands. the below is updating the
// `require` path for the two libraries. if there's a better way to share
// modules across both the repo and generated projects, we should make sure
// to remove/update this.
shell.sed('-i', /templates\/cordova\//, '', android_sdk_version);
shell.sed('-i', /templates\/cordova\//, '', check_reqs);
const templatesCordovaRegex = /templates\/cordova\//;
utils.replaceFileContents(android_sdk_version, templatesCordovaRegex, '');
utils.replaceFileContents(check_reqs, templatesCordovaRegex, '');
}
/**
@@ -197,19 +171,19 @@ function validatePackageName (package_name) {
var msg = 'Error validating package name. ';
if (!/^[a-zA-Z][a-zA-Z0-9_]+(\.[a-zA-Z][a-zA-Z0-9_]*)+$/.test(package_name)) {
return Q.reject(new CordovaError(msg + 'Must look like: `com.company.Name`. Currently is: `' + package_name + '`'));
return Promise.reject(new CordovaError(msg + 'Must look like: `com.company.Name`. Currently is: `' + package_name + '`'));
}
// Class is a reserved word
if (/\b[Cc]lass\b/.test(package_name)) {
return Q.reject(new CordovaError(msg + '"class" is a reserved word'));
return Promise.reject(new CordovaError(msg + '"class" is a reserved word'));
}
return Q.resolve();
return Promise.resolve();
}
/**
* Test whether a project name is acceptable for use as an android class.
* Test whether given string is acceptable for use as a project name
* Returns a promise, fulfilled if the project name is acceptable; rejected
* otherwise.
*/
@@ -217,20 +191,10 @@ function validateProjectName (project_name) {
var msg = 'Error validating project name. ';
// Make sure there's something there
if (project_name === '') {
return Q.reject(new CordovaError(msg + 'Project name cannot be empty'));
return Promise.reject(new CordovaError(msg + 'Project name cannot be empty'));
}
// Enforce stupid name error
if (project_name === 'CordovaActivity') {
return Q.reject(new CordovaError(msg + 'Project name cannot be CordovaActivity'));
}
// Classes in Java don't begin with numbers
if (/^[0-9]/.test(project_name)) {
return Q.reject(new CordovaError(msg + 'Project name must not begin with a number'));
}
return Q.resolve();
return Promise.resolve();
}
/**
@@ -252,19 +216,18 @@ function validateProjectName (project_name) {
* @return {Promise<String>} Directory where application has been created
*/
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'));
// Check if project already exists
if (fs.existsSync(project_path)) {
return Q.reject(new CordovaError('Project already exists! Delete and recreate'));
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';
var project_name = config.name()
? config.name().replace(/[^\w.]/g, '_') : 'CordovaExample';
var safe_activity_name = config.android_activityName() || options.activityName || 'MainActivity';
var target_api = check_reqs.get_target();
@@ -284,56 +247,55 @@ exports.create = function (project_path, config, options, events) {
events.emit('verbose', 'Copying android template project to ' + project_path);
exports.setShellFatal(true, function () {
var project_template_dir = options.customTemplate || path.join(ROOT, 'bin', 'templates', 'project');
var app_path = path.join(project_path, 'app', 'src', 'main');
var project_template_dir = options.customTemplate || path.join(ROOT, 'bin', 'templates', 'project');
var app_path = path.join(project_path, 'app', 'src', 'main');
// copy project template
shell.mkdir('-p', app_path);
shell.cp('-r', path.join(project_template_dir, 'assets'), app_path);
shell.cp('-r', path.join(project_template_dir, 'res'), app_path);
shell.cp(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore'));
// copy project template
fs.ensureDirSync(app_path);
fs.copySync(path.join(project_template_dir, 'assets'), path.join(app_path, 'assets'));
fs.copySync(path.join(project_template_dir, 'res'), path.join(app_path, 'res'));
fs.copySync(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore'));
// Manually create directories that would be empty within the template (since git doesn't track directories).
shell.mkdir(path.join(app_path, 'libs'));
// Manually create directories that would be empty within the template (since git doesn't track directories).
fs.ensureDirSync(path.join(app_path, 'libs'));
// copy cordova.js, cordova.jar
exports.copyJsAndLibrary(project_path, options.link, safe_activity_name);
// copy cordova.js, cordova.jar
exports.copyJsAndLibrary(project_path, options.link, safe_activity_name);
// 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');
shell.mkdir('-p', java_path);
shell.mkdir('-p', assets_path);
shell.mkdir('-p', resource_path);
// 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');
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');
// 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');
shell.mkdir('-p', activity_dir);
shell.cp('-f', path.join(project_template_dir, 'Activity.java'), activity_path);
shell.sed('-i', /__ACTIVITY__/, safe_activity_name, activity_path);
shell.sed('-i', /__NAME__/, project_name, path.join(app_path, 'res', 'values', 'strings.xml'));
shell.sed('-i', /__ID__/, package_name, activity_path);
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(activity_path, /__ID__/, package_name);
var manifest = new AndroidManifest(path.join(project_template_dir, 'AndroidManifest.xml'));
manifest.setPackageId(package_name)
.getActivity().setName(safe_activity_name);
var manifest = new AndroidManifest(path.join(project_template_dir, 'AndroidManifest.xml'));
manifest.setPackageId(package_name)
.getActivity().setName(safe_activity_name);
var manifest_path = path.join(app_path, 'AndroidManifest.xml');
manifest.write(manifest_path);
var manifest_path = path.join(app_path, 'AndroidManifest.xml');
manifest.write(manifest_path);
exports.copyScripts(project_path);
exports.copyBuildRules(project_path);
exports.copyScripts(project_path);
exports.copyBuildRules(project_path);
});
// Link it to local android install.
exports.writeProjectProperties(project_path, target_api);
exports.prepBuildFiles(project_path);
events.emit('log', generateDoneMessage('create', options.link));
}).thenResolve(project_path);
}).then(() => project_path);
};
function generateDoneMessage (type, link) {
@@ -347,7 +309,6 @@ function generateDoneMessage (type, link) {
// Returns a promise.
exports.update = function (projectPath, options, events) {
var errorString =
'An in-place platform update is not supported. \n' +
'The `platforms` folder is always treated as a build artifact in the CLI workflow.\n' +
@@ -358,5 +319,5 @@ exports.update = function (projectPath, options, events) {
'\tcordova platform add android\n'
;
return Q.reject(errorString);
return Promise.reject(errorString);
};

47
bin/lib/utils.js Normal file
View File

@@ -0,0 +1,47 @@
/*
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.
*/
/*
Provides a set of utility methods, which can also be spied on during unit tests.
*/
// TODO: Perhaps this should live in cordova-common?
const fs = require('fs-extra');
/**
* Reads, searches, and replaces the found occurences with replacementString and then writes the file back out.
* A backup is not made.
*
* @param {string} file A file path to a readable & writable file
* @param {RegExp} searchRegex The search regex
* @param {string} replacementString The string to replace the found occurences
* @returns {void}
*/
exports.replaceFileContents = function (file, searchRegex, replacementString) {
// let contents;
try {
var contents = fs.readFileSync(file).toString();
} catch (ex) {
console.log('TRYING TO READ: ', file);
throw ex;
}
contents = contents.replace(searchRegex, replacementString);
fs.writeFileSync(file, contents);
};

View File

@@ -17,8 +17,16 @@
under the License.
*/
/**
* @todo update coho to update this line.
* @todo use `package.json` instead but first
* figure out how this fit in with the platform-centered workflow structure.
* This workflow would not have the `package.json` file.
*/
// Coho updates this line
const VERSION = '9.0.0';
var path = require('path');
var Q = require('q');
var AndroidProject = require('./lib/AndroidProject');
var PluginManager = require('cordova-common').PluginManager;
@@ -26,6 +34,7 @@ var PluginManager = require('cordova-common').PluginManager;
var CordovaLogger = require('cordova-common').CordovaLogger;
var selfEvents = require('cordova-common').events;
var ConfigParser = require('cordova-common').ConfigParser;
const prepare = require('./lib/prepare').prepare;
var PLATFORM = 'android';
@@ -52,317 +61,324 @@ function setupEvents (externalEventEmitter) {
* The PlatformApi instance also should define the following field:
*
* * platform: String that defines a platform name.
* @class Api
*/
function Api (platform, platformRootDir, events) {
this.platform = PLATFORM;
this.root = path.resolve(__dirname, '..');
class Api {
constructor (platform, platformRootDir, events) {
this.platform = PLATFORM;
this.root = path.resolve(__dirname, '..');
setupEvents(events);
setupEvents(events);
const appMain = path.join(this.root, 'app', 'src', 'main');
const appRes = path.join(appMain, 'res');
const appMain = path.join(this.root, 'app', 'src', 'main');
const appRes = path.join(appMain, 'res');
this.locations = {
root: this.root,
www: path.join(appMain, 'assets', 'www'),
res: appRes,
platformWww: path.join(this.root, 'platform_www'),
configXml: path.join(appRes, 'xml', 'config.xml'),
defaultConfigXml: path.join(this.root, 'cordova', 'defaults.xml'),
strings: path.join(appRes, 'values', 'strings.xml'),
manifest: path.join(appMain, 'AndroidManifest.xml'),
build: path.join(this.root, 'build'),
javaSrc: path.join(appMain, 'java')
};
}
/**
* Installs platform to specified directory and creates a platform project.
*
* @param {String} destination Destination directory, where insatll platform to
* @param {ConfigParser} [config] ConfgiParser instance, used to retrieve
* project creation options, such as package id and project name.
* @param {Object} [options] An options object. The most common options are:
* @param {String} [options.customTemplate] A path to custom template, that
* should override the default one from platform.
* @param {Boolean} [options.link] Flag that indicates that platform's
* sources will be linked to installed platform instead of copying.
* @param {EventEmitter} [events] An EventEmitter instance that will be used for
* logging purposes. If no EventEmitter provided, all events will be logged to
* console
*
* @return {Promise<PlatformApi>} Promise either fulfilled with PlatformApi
* instance or rejected with CordovaError.
*/
Api.createPlatform = function (destination, config, options, events) {
events = setupEvents(events);
var result;
try {
result = require('../../lib/create').create(destination, config, options, events).then(function (destination) {
return new Api(PLATFORM, destination, events);
});
} catch (e) {
events.emit('error', 'createPlatform is not callable from the android project API.');
throw (e);
}
return result;
};
/**
* Updates already installed platform.
*
* @param {String} destination Destination directory, where platform installed
* @param {Object} [options] An options object. The most common options are:
* @param {String} [options.customTemplate] A path to custom template, that
* should override the default one from platform.
* @param {Boolean} [options.link] Flag that indicates that platform's
* sources will be linked to installed platform instead of copying.
* @param {EventEmitter} [events] An EventEmitter instance that will be used for
* logging purposes. If no EventEmitter provided, all events will be logged to
* console
*
* @return {Promise<PlatformApi>} Promise either fulfilled with PlatformApi
* instance or rejected with CordovaError.
*/
Api.updatePlatform = function (destination, options, events) {
events = setupEvents(events);
var result;
try {
result = require('../../lib/create').update(destination, options, events).then(function (destination) {
return new Api(PLATFORM, destination, events);
});
} catch (e) {
events.emit('error', 'updatePlatform is not callable from the android project API, you will need to do this manually.');
throw (e);
}
return result;
};
/**
* Gets a CordovaPlatform object, that represents the platform structure.
*
* @return {CordovaPlatform} A structure that contains the description of
* platform's file structure and other properties of platform.
*/
Api.prototype.getPlatformInfo = function () {
var result = {};
result.locations = this.locations;
result.root = this.root;
result.name = this.platform;
result.version = require('./version');
result.projectConfig = this._config;
return result;
};
/**
* Updates installed platform with provided www assets and new app
* configuration. This method is required for CLI workflow and will be called
* each time before build, so the changes, made to app configuration and www
* code, will be applied to platform.
*
* @param {CordovaProject} cordovaProject A CordovaProject instance, that defines a
* project structure and configuration, that should be applied to platform
* (contains project's www location and ConfigParser instance for project's
* config).
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError instance.
*/
Api.prototype.prepare = function (cordovaProject, prepareOptions) {
cordovaProject.projectConfig = new ConfigParser(cordovaProject.locations.rootConfigXml || cordovaProject.projectConfig.path);
return require('./lib/prepare').prepare.call(this, cordovaProject, prepareOptions);
};
/**
* Installs a new plugin into platform. This method only copies non-www files
* (sources, libs, etc.) to platform. It also doesn't resolves the
* dependencies of plugin. Both of handling of www files, such as assets and
* js-files and resolving dependencies are the responsibility of caller.
*
* @param {PluginInfo} plugin A PluginInfo instance that represents plugin
* that will be installed.
* @param {Object} installOptions An options object. Possible options below:
* @param {Boolean} installOptions.link: Flag that specifies that plugin
* sources will be symlinked to app's directory instead of copying (if
* possible).
* @param {Object} installOptions.variables An object that represents
* variables that will be used to install plugin. See more details on plugin
* variables in documentation:
* https://cordova.apache.org/docs/en/4.0.0/plugin_ref_spec.md.html
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError instance.
*/
Api.prototype.addPlugin = function (plugin, installOptions) {
var project = AndroidProject.getProjectFile(this.root);
var self = this;
installOptions = installOptions || {};
installOptions.variables = installOptions.variables || {};
// Add PACKAGE_NAME variable into vars
if (!installOptions.variables.PACKAGE_NAME) {
installOptions.variables.PACKAGE_NAME = project.getPackageName();
this.locations = {
root: this.root,
www: path.join(appMain, 'assets', 'www'),
res: appRes,
platformWww: path.join(this.root, 'platform_www'),
configXml: path.join(appRes, 'xml', 'config.xml'),
defaultConfigXml: path.join(this.root, 'cordova', 'defaults.xml'),
strings: path.join(appRes, 'values', 'strings.xml'),
manifest: path.join(appMain, 'AndroidManifest.xml'),
build: path.join(this.root, 'build'),
javaSrc: path.join(appMain, 'java')
};
}
return Q().then(function () {
return PluginManager.get(self.platform, self.locations, project).addPlugin(plugin, installOptions);
}).then(function () {
if (plugin.getFrameworks(this.platform).length === 0) return;
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
// This should pick the correct builder, not just get gradle
require('./lib/builders/builders').getBuilder().prepBuildFiles();
}.bind(this))
// CB-11022 Return truthy value to prevent running prepare after
.thenResolve(true);
};
/**
* Gets a CordovaPlatform object, that represents the platform structure.
*
* @return {CordovaPlatform} A structure that contains the description of
* platform's file structure and other properties of platform.
*/
getPlatformInfo () {
var result = {};
result.locations = this.locations;
result.root = this.root;
result.name = this.platform;
result.version = Api.version();
result.projectConfig = this._config;
/**
* Removes an installed plugin from platform.
*
* Since method accepts PluginInfo instance as input parameter instead of plugin
* id, caller shoud take care of managing/storing PluginInfo instances for
* future uninstalls.
*
* @param {PluginInfo} plugin A PluginInfo instance that represents plugin
* that will be installed.
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError instance.
*/
Api.prototype.removePlugin = function (plugin, uninstallOptions) {
var project = AndroidProject.getProjectFile(this.root);
if (uninstallOptions && uninstallOptions.usePlatformWww === true) {
uninstallOptions.usePlatformWww = false;
return result;
}
return PluginManager.get(this.platform, this.locations, project)
.removePlugin(plugin, uninstallOptions)
.then(function () {
/**
* Updates installed platform with provided www assets and new app
* configuration. This method is required for CLI workflow and will be called
* each time before build, so the changes, made to app configuration and www
* code, will be applied to platform.
*
* @param {CordovaProject} cordovaProject A CordovaProject instance, that defines a
* project structure and configuration, that should be applied to platform
* (contains project's www location and ConfigParser instance for project's
* config).
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError instance.
*/
prepare (cordovaProject, prepareOptions) {
cordovaProject.projectConfig = new ConfigParser(cordovaProject.locations.rootConfigXml || cordovaProject.projectConfig.path);
return prepare.call(this, cordovaProject, prepareOptions);
}
/**
* Installs a new plugin into platform. This method only copies non-www files
* (sources, libs, etc.) to platform. It also doesn't resolves the
* dependencies of plugin. Both of handling of www files, such as assets and
* js-files and resolving dependencies are the responsibility of caller.
*
* @param {PluginInfo} plugin A PluginInfo instance that represents plugin
* that will be installed.
* @param {Object} installOptions An options object. Possible options below:
* @param {Boolean} installOptions.link: Flag that specifies that plugin
* sources will be symlinked to app's directory instead of copying (if
* possible).
* @param {Object} installOptions.variables An object that represents
* variables that will be used to install plugin. See more details on plugin
* variables in documentation:
* https://cordova.apache.org/docs/en/4.0.0/plugin_ref_spec.md.html
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError instance.
*/
addPlugin (plugin, installOptions) {
var project = AndroidProject.getProjectFile(this.root);
var self = this;
installOptions = installOptions || {};
installOptions.variables = installOptions.variables || {};
// Add PACKAGE_NAME variable into vars
if (!installOptions.variables.PACKAGE_NAME) {
installOptions.variables.PACKAGE_NAME = project.getPackageName();
}
return Promise.resolve().then(function () {
return PluginManager.get(self.platform, self.locations, project).addPlugin(plugin, installOptions);
}).then(function () {
if (plugin.getFrameworks(this.platform).length === 0) return;
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
// This should pick the correct builder, not just get gradle
require('./lib/builders/builders').getBuilder().prepBuildFiles();
}.bind(this))
// CB-11022 Return truthy value to prevent running prepare after
.thenResolve(true);
};
/**
* Builds an application package for current platform.
*
* @param {Object} buildOptions A build options. This object's structure is
* highly depends on platform's specific. The most common options are:
* @param {Boolean} buildOptions.debug Indicates that packages should be
* built with debug configuration. This is set to true by default unless the
* 'release' option is not specified.
* @param {Boolean} buildOptions.release Indicates that packages should be
* built with release configuration. If not set to true, debug configuration
* will be used.
* @param {Boolean} buildOptions.device Specifies that built app is intended
* to run on device
* @param {Boolean} buildOptions.emulator: Specifies that built app is
* intended to run on emulator
* @param {String} buildOptions.target Specifies the device id that will be
* used to run built application.
* @param {Boolean} buildOptions.nobuild Indicates that this should be a
* dry-run call, so no build artifacts will be produced.
* @param {String[]} buildOptions.archs Specifies chip architectures which
* app packages should be built for. List of valid architectures is depends on
* platform.
* @param {String} buildOptions.buildConfig The path to build configuration
* file. The format of this file is depends on platform.
* @param {String[]} buildOptions.argv Raw array of command-line arguments,
* passed to `build` command. The purpose of this property is to pass a
* platform-specific arguments, and eventually let platform define own
* arguments processing logic.
*
* @return {Promise<Object[]>} A promise either fulfilled with an array of build
* artifacts (application packages) if package was built successfully,
* or rejected with CordovaError. The resultant build artifact objects is not
* strictly typed and may conatin arbitrary set of fields as in sample below.
*
* {
* architecture: 'x86',
* buildType: 'debug',
* path: '/path/to/build',
* type: 'app'
* }
*
* The return value in most cases will contain only one item but in some cases
* there could be multiple items in output array, e.g. when multiple
* arhcitectures is specified.
*/
Api.prototype.build = function (buildOptions) {
var self = this;
return require('./lib/check_reqs').run().then(function () {
return require('./lib/build').run.call(self, buildOptions);
}).then(function (buildResults) {
// Cast build result to array of build artifacts
return buildResults.paths.map(function (apkPath) {
return {
buildType: buildResults.buildType,
buildMethod: buildResults.buildMethod,
path: apkPath,
type: path.extname(apkPath).replace(/\./g, '')
};
});
});
};
/**
* Builds an application package for current platform and runs it on
* specified/default device. If no 'device'/'emulator'/'target' options are
* specified, then tries to run app on default device if connected, otherwise
* runs the app on emulator.
*
* @param {Object} runOptions An options object. The structure is the same
* as for build options.
*
* @return {Promise} A promise either fulfilled if package was built and ran
* successfully, or rejected with CordovaError.
*/
Api.prototype.run = function (runOptions) {
var self = this;
return require('./lib/check_reqs').run().then(function () {
return require('./lib/run').run.call(self, runOptions);
});
};
/**
* Cleans out the build artifacts from platform's directory, and also
* cleans out the platform www directory if called without options specified.
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError.
*/
Api.prototype.clean = function (cleanOptions) {
var self = this;
// This will lint, checking for null won't
if (typeof cleanOptions === 'undefined') {
cleanOptions = {};
// CB-11022 Return truthy value to prevent running prepare after
.then(() => true);
}
return require('./lib/check_reqs').run().then(function () {
return require('./lib/build').runClean.call(self, cleanOptions);
}).then(function () {
return require('./lib/prepare').clean.call(self, cleanOptions);
});
};
/**
* Removes an installed plugin from platform.
*
* Since method accepts PluginInfo instance as input parameter instead of plugin
* id, caller shoud take care of managing/storing PluginInfo instances for
* future uninstalls.
*
* @param {PluginInfo} plugin A PluginInfo instance that represents plugin
* that will be installed.
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError instance.
*/
removePlugin (plugin, uninstallOptions) {
var project = AndroidProject.getProjectFile(this.root);
/**
* Performs a requirements check for current platform. Each platform defines its
* own set of requirements, which should be resolved before platform can be
* built successfully.
*
* @return {Promise<Requirement[]>} Promise, resolved with set of Requirement
* objects for current platform.
*/
Api.prototype.requirements = function () {
return require('./lib/check_reqs').check_all();
};
if (uninstallOptions && uninstallOptions.usePlatformWww === true) {
uninstallOptions.usePlatformWww = false;
}
return PluginManager.get(this.platform, this.locations, project)
.removePlugin(plugin, uninstallOptions)
.then(function () {
if (plugin.getFrameworks(this.platform).length === 0) return;
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
require('./lib/builders/builders').getBuilder().prepBuildFiles();
}.bind(this))
// CB-11022 Return truthy value to prevent running prepare after
.then(() => true);
}
/**
* Builds an application package for current platform.
*
* @param {Object} buildOptions A build options. This object's structure is
* highly depends on platform's specific. The most common options are:
* @param {Boolean} buildOptions.debug Indicates that packages should be
* built with debug configuration. This is set to true by default unless the
* 'release' option is not specified.
* @param {Boolean} buildOptions.release Indicates that packages should be
* built with release configuration. If not set to true, debug configuration
* will be used.
* @param {Boolean} buildOptions.device Specifies that built app is intended
* to run on device
* @param {Boolean} buildOptions.emulator: Specifies that built app is
* intended to run on emulator
* @param {String} buildOptions.target Specifies the device id that will be
* used to run built application.
* @param {Boolean} buildOptions.nobuild Indicates that this should be a
* dry-run call, so no build artifacts will be produced.
* @param {String[]} buildOptions.archs Specifies chip architectures which
* app packages should be built for. List of valid architectures is depends on
* platform.
* @param {String} buildOptions.buildConfig The path to build configuration
* file. The format of this file is depends on platform.
* @param {String[]} buildOptions.argv Raw array of command-line arguments,
* passed to `build` command. The purpose of this property is to pass a
* platform-specific arguments, and eventually let platform define own
* arguments processing logic.
*
* @return {Promise<Object[]>} A promise either fulfilled with an array of build
* artifacts (application packages) if package was built successfully,
* or rejected with CordovaError. The resultant build artifact objects is not
* strictly typed and may conatin arbitrary set of fields as in sample below.
*
* {
* architecture: 'x86',
* buildType: 'debug',
* path: '/path/to/build',
* type: 'app'
* }
*
* The return value in most cases will contain only one item but in some cases
* there could be multiple items in output array, e.g. when multiple
* arhcitectures is specified.
*/
build (buildOptions) {
var self = this;
return require('./lib/check_reqs').run().then(function () {
return require('./lib/build').run.call(self, buildOptions);
}).then(function (buildResults) {
// Cast build result to array of build artifacts
return buildResults.paths.map(function (apkPath) {
return {
buildType: buildResults.buildType,
buildMethod: buildResults.buildMethod,
path: apkPath,
type: path.extname(apkPath).replace(/\./g, '')
};
});
});
}
/**
* Builds an application package for current platform and runs it on
* specified/default device. If no 'device'/'emulator'/'target' options are
* specified, then tries to run app on default device if connected, otherwise
* runs the app on emulator.
*
* @param {Object} runOptions An options object. The structure is the same
* as for build options.
*
* @return {Promise} A promise either fulfilled if package was built and ran
* successfully, or rejected with CordovaError.
*/
run (runOptions) {
var self = this;
return require('./lib/check_reqs').run().then(function () {
return require('./lib/run').run.call(self, runOptions);
});
}
/**
* Cleans out the build artifacts from platform's directory, and also
* cleans out the platform www directory if called without options specified.
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError.
*/
clean (cleanOptions) {
var self = this;
// This will lint, checking for null won't
if (typeof cleanOptions === 'undefined') {
cleanOptions = {};
}
return require('./lib/check_reqs').run().then(function () {
return require('./lib/build').runClean.call(self, cleanOptions);
}).then(function () {
return require('./lib/prepare').clean.call(self, cleanOptions);
});
}
/**
* Performs a requirements check for current platform. Each platform defines its
* own set of requirements, which should be resolved before platform can be
* built successfully.
*
* @return {Promise<Requirement[]>} Promise, resolved with set of Requirement
* objects for current platform.
*/
requirements () {
return require('./lib/check_reqs').check_all();
}
/**
* Installs platform to specified directory and creates a platform project.
*
* @param {String} destination Destination directory, where insatll platform to
* @param {ConfigParser} [config] ConfgiParser instance, used to retrieve
* project creation options, such as package id and project name.
* @param {Object} [options] An options object. The most common options are:
* @param {String} [options.customTemplate] A path to custom template, that
* should override the default one from platform.
* @param {Boolean} [options.link] Flag that indicates that platform's
* sources will be linked to installed platform instead of copying.
* @param {EventEmitter} [events] An EventEmitter instance that will be used for
* logging purposes. If no EventEmitter provided, all events will be logged to
* console
*
* @return {Promise<PlatformApi>} Promise either fulfilled with PlatformApi
* instance or rejected with CordovaError.
*/
static createPlatform (destination, config, options, events) {
events = setupEvents(events);
var result;
try {
result = require('../../lib/create').create(destination, config, options, events).then(function (destination) {
return new Api(PLATFORM, destination, events);
});
} catch (e) {
events.emit('error', 'createPlatform is not callable from the android project API.');
throw (e);
}
return result;
}
/**
* Updates already installed platform.
*
* @param {String} destination Destination directory, where platform installed
* @param {Object} [options] An options object. The most common options are:
* @param {String} [options.customTemplate] A path to custom template, that
* should override the default one from platform.
* @param {Boolean} [options.link] Flag that indicates that platform's
* sources will be linked to installed platform instead of copying.
* @param {EventEmitter} [events] An EventEmitter instance that will be used for
* logging purposes. If no EventEmitter provided, all events will be logged to
* console
*
* @return {Promise<PlatformApi>} Promise either fulfilled with PlatformApi
* instance or rejected with CordovaError.
*/
static updatePlatform (destination, options, events) {
events = setupEvents(events);
var result;
try {
result = require('../../lib/create').update(destination, options, events).then(function (destination) {
return new Api(PLATFORM, destination, events);
});
} catch (e) {
events.emit('error', 'updatePlatform is not callable from the android project API, you will need to do this manually.');
throw (e);
}
return result;
}
static version () {
return VERSION;
}
}
module.exports = Api;

View File

@@ -31,13 +31,13 @@ if (['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(args[2]) >= 0) {
// Do some basic argument parsing
var buildOpts = nopt({
'verbose': Boolean,
'silent': Boolean,
'debug': Boolean,
'release': Boolean,
'nobuild': Boolean,
'buildConfig': path
}, { 'd': '--verbose' });
verbose: Boolean,
silent: Boolean,
debug: Boolean,
release: Boolean,
nobuild: Boolean,
buildConfig: path
}, { d: '--verbose' });
// Make buildOptions compatible with PlatformApi build method spec
buildOpts.argv = buildOpts.argv.original;

View File

@@ -32,9 +32,9 @@ if (['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >=
// Do some basic argument parsing
var opts = nopt({
'verbose': Boolean,
'silent': Boolean
}, { 'd': '--verbose' });
verbose: Boolean,
silent: Boolean
}, { d: '--verbose' });
// Make buildOptions compatible with PlatformApi clean method spec
opts.argv = opts.argv.original;

View File

@@ -17,10 +17,9 @@
under the License.
*/
var Q = require('q');
var os = require('os');
var execa = require('execa');
var events = require('cordova-common').events;
var spawn = require('cordova-common').superspawn.spawn;
var CordovaError = require('cordova-common').CordovaError;
var Adb = {};
@@ -44,7 +43,7 @@ function isEmulator (line) {
* devices/emulators
*/
Adb.devices = function (opts) {
return spawn('adb', ['devices'], { cwd: os.tmpdir() }).then(function (output) {
return execa('adb', ['devices'], { cwd: os.tmpdir() }).then(({ stdout: output }) => {
return output.split('\n').filter(function (line) {
// Filter out either real devices or emulators, depending on options
return (line && opts && opts.emulators) ? isEmulator(line) : isDevice(line);
@@ -58,7 +57,7 @@ Adb.install = function (target, packagePath, opts) {
events.emit('verbose', 'Installing apk ' + packagePath + ' on target ' + target + '...');
var args = ['-s', target, 'install'];
if (opts && opts.replace) args.push('-r');
return spawn('adb', args.concat(packagePath), { cwd: os.tmpdir() }).then(function (output) {
return execa('adb', args.concat(packagePath), { cwd: os.tmpdir() }).then(({ stdout: output }) => {
// 'adb install' seems to always returns no error, even if installation fails
// so we catching output to detect installation failure
if (output.match(/Failure/)) {
@@ -70,31 +69,30 @@ Adb.install = function (target, packagePath, opts) {
'\nEither uninstall an app or increment the versionCode.';
}
return Q.reject(new CordovaError('Failed to install apk to device: ' + output));
return Promise.reject(new CordovaError('Failed to install apk to device: ' + output));
}
});
};
Adb.uninstall = function (target, packageId) {
events.emit('verbose', 'Uninstalling package ' + packageId + ' from target ' + target + '...');
return spawn('adb', ['-s', target, 'uninstall', packageId], { cwd: os.tmpdir() });
return execa('adb', ['-s', target, 'uninstall', packageId], { cwd: os.tmpdir() }).then(({ stdout }) => stdout);
};
Adb.shell = function (target, shellCommand) {
events.emit('verbose', 'Running adb shell command "' + shellCommand + '" on target ' + target + '...');
var args = ['-s', target, 'shell'];
shellCommand = shellCommand.split(/\s+/);
return spawn('adb', args.concat(shellCommand), { cwd: os.tmpdir() }).catch(function (output) {
return Q.reject(new CordovaError('Failed to execute shell command "' +
shellCommand + '"" on device: ' + output));
});
return execa('adb', args.concat(shellCommand), { cwd: os.tmpdir() })
.then(({ stdout }) => stdout)
.catch(error => Promise.reject(new CordovaError(`Failed to execute shell command "${shellCommand}" on device: ${error}`)));
};
Adb.start = function (target, activityName) {
events.emit('verbose', 'Starting application "' + activityName + '" on target ' + target + '...');
return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName).catch(function (output) {
return Q.reject(new CordovaError('Failed to start application "' +
activityName + '"" on device: ' + output));
return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName).catch((error) => {
return Promise.reject(new CordovaError('Failed to start application "' +
activityName + '"" on device: ' + error));
});
};

View File

@@ -23,104 +23,106 @@ var xml = require('cordova-common').xmlHelpers;
var DEFAULT_ORIENTATION = 'default';
/** Wraps an AndroidManifest file */
function AndroidManifest (path) {
this.path = path;
this.doc = xml.parseElementtreeSync(path);
if (this.doc.getroot().tag !== 'manifest') {
throw new Error('AndroidManifest at ' + path + ' has incorrect root node name (expected "manifest")');
class AndroidManifest {
constructor (path) {
this.path = path;
this.doc = xml.parseElementtreeSync(path);
if (this.doc.getroot().tag !== 'manifest') {
throw new Error('AndroidManifest at ' + path + ' has incorrect root node name (expected "manifest")');
}
}
getVersionName () {
return this.doc.getroot().attrib['android:versionName'];
}
setVersionName (versionName) {
this.doc.getroot().attrib['android:versionName'] = versionName;
return this;
}
getVersionCode () {
return this.doc.getroot().attrib['android:versionCode'];
}
setVersionCode (versionCode) {
this.doc.getroot().attrib['android:versionCode'] = versionCode;
return this;
}
getPackageId () {
return this.doc.getroot().attrib.package;
}
setPackageId (pkgId) {
this.doc.getroot().attrib.package = pkgId;
return this;
}
getActivity () {
var activity = this.doc.getroot().find('./application/activity');
return {
getName: function () {
return activity.attrib['android:name'];
},
setName: function (name) {
if (!name) {
delete activity.attrib['android:name'];
} else {
activity.attrib['android:name'] = name;
}
return this;
},
getOrientation: function () {
return activity.attrib['android:screenOrientation'];
},
setOrientation: function (orientation) {
if (!orientation || orientation.toLowerCase() === DEFAULT_ORIENTATION) {
delete activity.attrib['android:screenOrientation'];
} else {
activity.attrib['android:screenOrientation'] = orientation;
}
return this;
},
getLaunchMode: function () {
return activity.attrib['android:launchMode'];
},
setLaunchMode: function (launchMode) {
if (!launchMode) {
delete activity.attrib['android:launchMode'];
} else {
activity.attrib['android:launchMode'] = launchMode;
}
return this;
}
};
}
getDebuggable () {
return this.doc.getroot().find('./application').attrib['android:debuggable'] === 'true';
}
setDebuggable (value) {
var application = this.doc.getroot().find('./application');
if (value) {
application.attrib['android:debuggable'] = 'true';
} else {
// The default value is "false", so we can remove attribute at all.
delete application.attrib['android:debuggable'];
}
return this;
}
/**
* Writes manifest to disk syncronously. If filename is specified, then manifest
* will be written to that file
*
* @param {String} [destPath] File to write manifest to. If omitted,
* manifest will be written to file it has been read from.
*/
write (destPath) {
fs.writeFileSync(destPath || this.path, this.doc.write({ indent: 4 }), 'utf-8');
}
}
AndroidManifest.prototype.getVersionName = function () {
return this.doc.getroot().attrib['android:versionName'];
};
AndroidManifest.prototype.setVersionName = function (versionName) {
this.doc.getroot().attrib['android:versionName'] = versionName;
return this;
};
AndroidManifest.prototype.getVersionCode = function () {
return this.doc.getroot().attrib['android:versionCode'];
};
AndroidManifest.prototype.setVersionCode = function (versionCode) {
this.doc.getroot().attrib['android:versionCode'] = versionCode;
return this;
};
AndroidManifest.prototype.getPackageId = function () {
return this.doc.getroot().attrib['package'];
};
AndroidManifest.prototype.setPackageId = function (pkgId) {
this.doc.getroot().attrib['package'] = pkgId;
return this;
};
AndroidManifest.prototype.getActivity = function () {
var activity = this.doc.getroot().find('./application/activity');
return {
getName: function () {
return activity.attrib['android:name'];
},
setName: function (name) {
if (!name) {
delete activity.attrib['android:name'];
} else {
activity.attrib['android:name'] = name;
}
return this;
},
getOrientation: function () {
return activity.attrib['android:screenOrientation'];
},
setOrientation: function (orientation) {
if (!orientation || orientation.toLowerCase() === DEFAULT_ORIENTATION) {
delete activity.attrib['android:screenOrientation'];
} else {
activity.attrib['android:screenOrientation'] = orientation;
}
return this;
},
getLaunchMode: function () {
return activity.attrib['android:launchMode'];
},
setLaunchMode: function (launchMode) {
if (!launchMode) {
delete activity.attrib['android:launchMode'];
} else {
activity.attrib['android:launchMode'] = launchMode;
}
return this;
}
};
};
AndroidManifest.prototype.getDebuggable = function () {
return this.doc.getroot().find('./application').attrib['android:debuggable'] === 'true';
};
AndroidManifest.prototype.setDebuggable = function (value) {
var application = this.doc.getroot().find('./application');
if (value) {
application.attrib['android:debuggable'] = 'true';
} else {
// The default value is "false", so we can remove attribute at all.
delete application.attrib['android:debuggable'];
}
return this;
};
/**
* Writes manifest to disk syncronously. If filename is specified, then manifest
* will be written to that file
*
* @param {String} [destPath] File to write manifest to. If omitted,
* manifest will be written to file it has been read from.
*/
AndroidManifest.prototype.write = function (destPath) {
fs.writeFileSync(destPath || this.path, this.doc.write({ indent: 4 }), 'utf-8');
};
module.exports = AndroidManifest;

View File

@@ -55,148 +55,148 @@ function getRelativeLibraryPath (parentDir, subDir) {
return (path.sep === '\\') ? libraryPath.replace(/\\/g, '/') : libraryPath;
}
function AndroidProject (projectDir) {
this._propertiesEditors = {};
this._subProjectDirs = {};
this._dirty = false;
this.projectDir = projectDir;
this.platformWww = path.join(this.projectDir, 'platform_www');
this.www = path.join(this.projectDir, 'app/src/main/assets/www');
class AndroidProject {
constructor (projectDir) {
this._propertiesEditors = {};
this._subProjectDirs = {};
this._dirty = false;
this.projectDir = projectDir;
this.platformWww = path.join(this.projectDir, 'platform_www');
this.www = path.join(this.projectDir, 'app/src/main/assets/www');
}
/**
* Reads the package name out of the Android Manifest file
*
* @param {String} projectDir The absolute path to the directory containing the project
* @return {String} The name of the package
*/
getPackageName () {
var manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml');
return new AndroidManifest(manifestPath).getPackageId();
}
getCustomSubprojectRelativeDir (plugin_id, src) {
// All custom subprojects are prefixed with the last portion of the package id.
// This is to avoid collisions when opening multiple projects in Eclipse that have subprojects with the same name.
var packageName = this.getPackageName();
var lastDotIndex = packageName.lastIndexOf('.');
var prefix = packageName.substring(lastDotIndex + 1);
var subRelativeDir = path.join(plugin_id, prefix + '-' + path.basename(src));
return subRelativeDir;
}
addSubProject (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var subProjectFile = path.resolve(subDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
// TODO: Setting the target needs to happen only for pre-3.7.0 projects
if (fs.existsSync(subProjectFile)) {
var subProperties = this._getPropertiesFile(subProjectFile);
subProperties.set('target', parentProperties.get('target'));
subProperties.dirty = true;
this._subProjectDirs[subDir] = true;
}
addToPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
}
removeSubProject (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir));
delete this._subProjectDirs[subDir];
this._dirty = true;
}
addGradleReference (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
addToPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
}
removeGradleReference (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
}
addSystemLibrary (parentDir, value) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var 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);
removeFromPropertyList(parentProperties, 'cordova.system.library', value);
this._dirty = true;
}
write () {
if (!this._dirty) {
return;
}
this._dirty = false;
for (var filename in this._propertiesEditors) {
var editor = this._propertiesEditors[filename];
if (editor.dirty) {
fs.writeFileSync(filename, editor.toString());
editor.dirty = false;
}
}
}
getInstaller (type) {
return pluginHandlers.getInstaller(type);
}
getUninstaller (type) {
return pluginHandlers.getUninstaller(type);
}
/*
* This checks if an Android project is clean or has old build artifacts
*/
isClean () {
var build_path = path.join(this.projectDir, 'build');
// If the build directory doesn't exist, it's clean
return !(fs.existsSync(build_path));
}
_getPropertiesFile (filename) {
if (!this._propertiesEditors[filename]) {
if (fs.existsSync(filename)) {
this._propertiesEditors[filename] = properties_parser.createEditor(filename);
} else {
this._propertiesEditors[filename] = properties_parser.createEditor();
}
}
return this._propertiesEditors[filename];
}
static getProjectFile (projectDir) {
if (!projectFileCache[projectDir]) {
projectFileCache[projectDir] = new AndroidProject(projectDir);
}
return projectFileCache[projectDir];
}
static purgeCache (projectDir) {
if (projectDir) {
delete projectFileCache[projectDir];
} else {
projectFileCache = {};
}
}
}
AndroidProject.getProjectFile = function (projectDir) {
if (!projectFileCache[projectDir]) {
projectFileCache[projectDir] = new AndroidProject(projectDir);
}
return projectFileCache[projectDir];
};
AndroidProject.purgeCache = function (projectDir) {
if (projectDir) {
delete projectFileCache[projectDir];
} else {
projectFileCache = {};
}
};
/**
* Reads the package name out of the Android Manifest file
*
* @param {String} projectDir The absolute path to the directory containing the project
*
* @return {String} The name of the package
*/
AndroidProject.prototype.getPackageName = function () {
var manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml');
return new AndroidManifest(manifestPath).getPackageId();
};
AndroidProject.prototype.getCustomSubprojectRelativeDir = function (plugin_id, src) {
// All custom subprojects are prefixed with the last portion of the package id.
// This is to avoid collisions when opening multiple projects in Eclipse that have subprojects with the same name.
var packageName = this.getPackageName();
var lastDotIndex = packageName.lastIndexOf('.');
var prefix = packageName.substring(lastDotIndex + 1);
var subRelativeDir = path.join(plugin_id, prefix + '-' + path.basename(src));
return subRelativeDir;
};
AndroidProject.prototype.addSubProject = function (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var subProjectFile = path.resolve(subDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
// TODO: Setting the target needs to happen only for pre-3.7.0 projects
if (fs.existsSync(subProjectFile)) {
var subProperties = this._getPropertiesFile(subProjectFile);
subProperties.set('target', parentProperties.get('target'));
subProperties.dirty = true;
this._subProjectDirs[subDir] = true;
}
addToPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
};
AndroidProject.prototype.removeSubProject = function (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir));
delete this._subProjectDirs[subDir];
this._dirty = true;
};
AndroidProject.prototype.addGradleReference = function (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
addToPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
};
AndroidProject.prototype.removeGradleReference = function (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
};
AndroidProject.prototype.addSystemLibrary = function (parentDir, value) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
addToPropertyList(parentProperties, 'cordova.system.library', value);
this._dirty = true;
};
AndroidProject.prototype.removeSystemLibrary = function (parentDir, value) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'cordova.system.library', value);
this._dirty = true;
};
AndroidProject.prototype.write = function () {
if (!this._dirty) {
return;
}
this._dirty = false;
for (var filename in this._propertiesEditors) {
var editor = this._propertiesEditors[filename];
if (editor.dirty) {
fs.writeFileSync(filename, editor.toString());
editor.dirty = false;
}
}
};
AndroidProject.prototype._getPropertiesFile = function (filename) {
if (!this._propertiesEditors[filename]) {
if (fs.existsSync(filename)) {
this._propertiesEditors[filename] = properties_parser.createEditor(filename);
} else {
this._propertiesEditors[filename] = properties_parser.createEditor();
}
}
return this._propertiesEditors[filename];
};
AndroidProject.prototype.getInstaller = function (type) {
return pluginHandlers.getInstaller(type);
};
AndroidProject.prototype.getUninstaller = function (type) {
return pluginHandlers.getUninstaller(type);
};
/*
* This checks if an Android project is clean or has old build artifacts
*/
AndroidProject.prototype.isClean = function () {
var build_path = path.join(this.projectDir, 'build');
// If the build directory doesn't exist, it's clean
return !(fs.existsSync(build_path));
};
module.exports = AndroidProject;

View File

@@ -17,7 +17,7 @@
under the License.
*/
var superspawn = require('cordova-common').superspawn;
const execa = require('execa');
var suffix_number_regex = /(\d+)$/;
// Used for sorting Android targets, example strings to sort:
@@ -29,17 +29,14 @@ var suffix_number_regex = /(\d+)$/;
// the number at the end, the more recent the target, the closer to the
// start of the array.
function sort_by_largest_numerical_suffix (a, b) {
var suffix_a = a.match(suffix_number_regex);
var suffix_b = b.match(suffix_number_regex);
if (suffix_a && suffix_b) {
// If the two targets being compared have suffixes, return less than
// zero, or greater than zero, based on which suffix is larger.
return (parseInt(suffix_a[1]) > parseInt(suffix_b[1]) ? -1 : 1);
} else {
// If no suffix numbers were detected, leave the order as-is between
// elements a and b.
return 0;
}
let suffix_a = a.match(suffix_number_regex);
let suffix_b = b.match(suffix_number_regex);
// If no number is detected (eg: preview version like android-R),
// designate a suffix of 0 so it gets moved to the end
suffix_a = suffix_a || ['0', '0'];
suffix_b = suffix_b || ['0', '0'];
// Return < zero, or > zero, based on which suffix is larger.
return (parseInt(suffix_a[1]) > parseInt(suffix_b[1]) ? -1 : 1);
}
module.exports.print_newest_available_sdk_target = function () {
@@ -49,6 +46,8 @@ module.exports.print_newest_available_sdk_target = function () {
});
};
// Versions should not be represented as float, so we disable quote-props here
/* eslint-disable quote-props */
module.exports.version_string_to_api_level = {
'4.0': 14,
'4.0.3': 15,
@@ -64,6 +63,7 @@ module.exports.version_string_to_api_level = {
'7.1.1': 25,
'8.0': 26
};
/* eslint-enable quote-props */
function parse_targets (output) {
var target_out = output.split('\n');
@@ -77,11 +77,11 @@ function parse_targets (output) {
}
module.exports.list_targets_with_android = function () {
return superspawn.spawn('android', ['list', 'target']).then(parse_targets);
return execa('android', ['list', 'target']).then(result => parse_targets(result.stdout));
};
module.exports.list_targets_with_avdmanager = function () {
return superspawn.spawn('avdmanager', ['list', 'target']).then(parse_targets);
return execa('avdmanager', ['list', 'target']).then(result => parse_targets(result.stdout));
};
module.exports.list_targets = function () {

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -19,7 +17,6 @@
under the License.
*/
var Q = require('q');
var path = require('path');
var fs = require('fs');
var nopt = require('nopt');
@@ -28,7 +25,7 @@ var Adb = require('./Adb');
var builders = require('./builders/builders');
var events = require('cordova-common').events;
var spawn = require('cordova-common').superspawn.spawn;
const execa = require('execa');
var CordovaError = require('cordova-common').CordovaError;
var PackageType = require('./PackageType');
@@ -114,7 +111,7 @@ function parseOpts (options, resolvedTarget, projectRoot) {
let shouldWarn = false;
const signingKeys = ['keystore', 'alias', 'storePassword', 'password', 'keystoreType'];
for (let key in packageArgs) {
for (const key in packageArgs) {
if (!shouldWarn && signingKeys.indexOf(key) > -1) {
// If we enter this condition, we have a key used for signing a build,
// but we are missing some required signing properties
@@ -204,27 +201,37 @@ module.exports.detectArchitecture = function (target) {
return /intel/i.exec(output) ? 'x86' : 'arm';
});
}
function timeout (ms, err) {
return new Promise((resolve, reject) => {
setTimeout(() => reject(err), ms);
});
}
// It sometimes happens (at least on OS X), that this command will hang forever.
// To fix it, either unplug & replug device, or restart adb server.
return helper().timeout(1000, new CordovaError('Device communication timed out. Try unplugging & replugging the device.')).then(null, function (err) {
return Promise.race([
helper(),
timeout(5000, new CordovaError(
'Device communication timed out. Try unplugging & replugging the device.'
))
]).catch(err => {
if (/timed out/.exec('' + err)) {
// adb kill-server doesn't seem to do the trick.
// Could probably find a x-platform version of killall, but I'm not actually
// sure that this scenario even happens on non-OSX machines.
events.emit('verbose', 'adb timed out while detecting device/emulator architecture. Killing adb and trying again.');
return spawn('killall', ['adb']).then(function () {
return execa('killall', ['adb']).then(function () {
return helper().then(null, function () {
// The double kill is sadly often necessary, at least on mac.
events.emit('warn', 'adb timed out a second time while detecting device/emulator architecture. Killing adb and trying again.');
return spawn('killall', ['adb']).then(function () {
return execa('killall', ['adb']).then(function () {
return helper().then(null, function () {
return Q.reject(new CordovaError('adb timed out a third time while detecting device/emulator architecture. Try unplugging & replugging the device.'));
return Promise.reject(new CordovaError('adb timed out a third time while detecting device/emulator architecture. Try unplugging & replugging the device.'));
});
});
});
}, function () {
// For non-killall OS's.
return Q.reject(err);
return Promise.reject(err);
});
}
throw err;
@@ -255,45 +262,23 @@ module.exports.findBestApkForArchitecture = function (buildResults, arch) {
};
function PackageInfo (keystore, alias, storePassword, password, keystoreType) {
this.keystore = {
'name': 'key.store',
'value': keystore
};
this.alias = {
'name': 'key.alias',
'value': alias
};
if (storePassword) {
this.storePassword = {
'name': 'key.store.password',
'value': storePassword
};
}
if (password) {
this.password = {
'name': 'key.alias.password',
'value': password
};
}
if (keystoreType) {
this.keystoreType = {
'name': 'key.store.type',
'value': keystoreType
};
}
const createNameKeyObject = (name, value) => ({ name, value: value.replace(/\\/g, '\\\\') });
this.data = [
createNameKeyObject('key.store', keystore),
createNameKeyObject('key.alias', alias)
];
if (storePassword) this.data.push(createNameKeyObject('key.store.password', storePassword));
if (password) this.data.push(createNameKeyObject('key.alias.password', password));
if (keystoreType) this.data.push(createNameKeyObject('key.store.type', keystoreType));
}
PackageInfo.prototype = {
toProperties: function () {
var self = this;
var result = '';
Object.keys(self).forEach(function (key) {
result += self[key].name;
result += '=';
result += self[key].value.replace(/\\/g, '\\\\');
result += '\n';
});
return result;
appendToProperties: function (propertiesParser) {
for (const { name, value } of this.data) propertiesParser.set(name, value);
propertiesParser.save();
}
};

View File

@@ -16,19 +16,15 @@
specific language governing permissions and limitations
under the License.
*/
/* eslint no-self-assign: 0 */
/* eslint no-unused-vars: 0 */
var Q = require('q');
var fs = require('fs');
var fs = require('fs-extra');
var path = require('path');
var shell = require('shelljs');
var spawn = require('cordova-common').superspawn.spawn;
const execa = require('execa');
var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;
var check_reqs = require('../check_reqs');
var PackageType = require('../PackageType');
const compareFunc = require('compare-func');
const { createEditor } = require('properties-parser');
const MARKER = 'YOUR CHANGES WILL BE ERASED!';
const SIGNING_PROPERTIES = '-signing.properties';
@@ -36,6 +32,85 @@ const TEMPLATE =
'# This file is automatically generated.\n' +
'# Do not modify this file -- ' + MARKER + '\n';
const archSpecificRegex = /-x86|-arm/;
const unsignedBuildRegex = /-unsigned/;
const fileSorter = (filePathA, filePathB) => {
const archSpecificA = archSpecificRegex.test(filePathA);
const archSpecificB = archSpecificRegex.test(filePathB);
// If they are not equal, then sort by specific archs after generic ones
if (archSpecificA !== archSpecificB) {
return archSpecificA < archSpecificB ? -1 : 1;
}
// Otherwise, move onto the next sort item, which is by sorting unsigned bulds after signed ones
const unsignedA = unsignedBuildRegex.test(filePathA);
const unsignedB = unsignedBuildRegex.test(filePathB);
if (unsignedA !== unsignedB) {
return unsignedA < unsignedB ? -1 : 1;
}
// Then, sort by modification time, latest first
const modTimeA = fs.statSync(filePathA).mtime.getTime();
const modTimeB = fs.statSync(filePathB).mtime.getTime();
if (modTimeA !== modTimeB) {
return modTimeA < modTimeB ? 1 : -1;
}
// Finally, if all above is the same, sort by file name length, ascending
return filePathB.length < filePathA.length ? -1 : 1;
};
/**
* If the provided directory does not exist or extension is missing, return an empty array.
* If the director exists, loop the directories and collect list of files matching the extension.
*
* @param {String} dir Directory to scan
* @param {String} extension
*/
function recursivelyFindFiles (dir, extension) {
if (!fs.existsSync(dir) || !extension) return [];
const files = fs.readdirSync(dir, { withFileTypes: true })
.map(entry => {
const item = path.resolve(dir, entry.name);
if (entry.isDirectory()) return recursivelyFindFiles(item, extension);
if (path.extname(entry.name) === `.${extension}`) return item;
return false;
});
return Array.prototype.concat(...files)
.filter(file => file !== false);
}
/**
* @param {String} dir
* @param {String} build_type
* @param {String} arch
* @param {String} extension
*/
function findOutputFilesHelper (dir, build_type, arch, extension) {
let files = recursivelyFindFiles(path.resolve(dir, build_type), extension);
if (files.length === 0) return files;
// Assume arch-specific build if newest apk has -x86 or -arm.
const archSpecific = !!/-x86|-arm/.exec(path.basename(files[0]));
// And show only arch-specific ones (or non-arch-specific)
files = files.filter(p => !!/-x86|-arm/.exec(path.basename(p)) === archSpecific);
if (archSpecific && files.length > 1 && arch) {
files = files.filter(p => path.basename(p).indexOf('-' + arch) !== -1);
}
return files;
}
class ProjectBuilder {
constructor (rootDirectory) {
this.root = rootDirectory || path.resolve(__dirname, '../../..');
@@ -66,10 +141,10 @@ class ProjectBuilder {
if (opts.arch) {
args.push('-PcdvBuildArch=' + opts.arch);
}
args.push.apply(args, opts.extraArgs);
}
args.push.apply(args, opts.extraArgs);
return args;
}
@@ -82,7 +157,7 @@ class ProjectBuilder {
if (fs.existsSync(gradlePath)) {
// Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
} else {
return spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], { stdio: 'inherit' });
return execa(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], { stdio: 'inherit' });
}
}
@@ -134,7 +209,7 @@ class ProjectBuilder {
try {
fs.accessSync(subProjectGradle, fs.F_OK);
} catch (e) {
shell.cp('-f', pluginBuildGradle, subProjectGradle);
fs.copySync(pluginBuildGradle, subProjectGradle);
}
};
@@ -224,22 +299,25 @@ class ProjectBuilder {
return self.runGradleWrapper(gradlePath);
}).then(function () {
return self.prepBuildFiles();
}).then(function () {
// If the gradle distribution URL is set, make sure it points to version we want.
// If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with.
// For some reason, using ^ and $ don't work. This does the job, though.
var distributionUrlRegex = /distributionUrl.*zip/;
var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-4.10.3-all.zip';
var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties');
shell.chmod('u+w', gradleWrapperPropertiesPath);
shell.sed('-i', distributionUrlRegex, 'distributionUrl=' + distributionUrl, gradleWrapperPropertiesPath);
}).then(() => {
// update/set the distributionUrl in the gradle-wrapper.properties
const gradleWrapperPropertiesPath = path.join(self.root, 'gradle/wrapper/gradle-wrapper.properties');
const gradleWrapperProperties = createEditor(gradleWrapperPropertiesPath);
const distributionUrl = process.env.CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL || 'https://services.gradle.org/distributions/gradle-6.5-all.zip';
gradleWrapperProperties.set('distributionUrl', distributionUrl);
gradleWrapperProperties.save();
var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
var propertiesFilePath = path.join(self.root, propertiesFile);
events.emit('verbose', `Gradle Distribution URL: ${distributionUrl}`);
})
.then(() => {
const signingPropertiesPath = path.join(self.root, `${opts.buildType}${SIGNING_PROPERTIES}`);
if (fs.existsSync(signingPropertiesPath)) fs.removeSync(signingPropertiesPath);
if (opts.packageInfo) {
fs.writeFileSync(propertiesFilePath, TEMPLATE + opts.packageInfo.toProperties());
} else if (isAutoGenerated(propertiesFilePath)) {
shell.rm('-f', propertiesFilePath);
fs.ensureFileSync(signingPropertiesPath);
const signingProperties = createEditor(signingPropertiesPath);
signingProperties.addHeadComment(TEMPLATE);
opts.packageInfo.appendToProperties(signingProperties);
}
});
}
@@ -252,61 +330,43 @@ class ProjectBuilder {
var wrapper = path.join(this.root, 'gradlew');
var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
return spawn(wrapper, args, { stdio: 'pipe' })
.progress(function (stdio) {
if (stdio.stderr) {
/*
* Workaround for the issue with Java printing some unwanted information to
* stderr instead of stdout.
* This function suppresses 'Picked up _JAVA_OPTIONS' message from being
* printed to stderr. See https://issues.apache.org/jira/browse/CB-9971 for
* explanation.
*/
var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(stdio.stderr.toString());
if (suppressThisLine) {
return;
}
process.stderr.write(stdio.stderr);
} else {
process.stdout.write(stdio.stdout);
}
}).catch(function (error) {
return execa(wrapper, args, { stdio: 'inherit' })
.catch(function (error) {
if (error.toString().indexOf('failed to find target with hash string') >= 0) {
return check_reqs.check_android_target(error).then(function () {
// If due to some odd reason - check_android_target succeeds
// we should still fail here.
return Q.reject(error);
throw error;
});
}
return Q.reject(error);
throw error;
});
}
clean (opts) {
var builder = this;
var wrapper = path.join(this.root, 'gradlew');
var args = builder.getArgs('clean', opts);
return Q().then(function () {
return spawn(wrapper, args, { stdio: 'inherit' });
})
.then(function () {
shell.rm('-rf', path.join(builder.root, 'out'));
const wrapper = path.join(this.root, 'gradlew');
const args = this.getArgs('clean', opts);
return execa(wrapper, args, { stdio: 'inherit' })
.then(() => {
fs.removeSync(path.join(this.root, 'out'));
['debug', 'release'].forEach(function (config) {
var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES);
if (isAutoGenerated(propertiesFilePath)) {
shell.rm('-f', propertiesFilePath);
}
});
['debug', 'release'].map(config => path.join(this.root, `${config}${SIGNING_PROPERTIES}`))
.forEach(file => {
const hasFile = fs.existsSync(file);
const hasMarker = hasFile && fs.readFileSync(file, 'utf8')
.includes(MARKER);
if (hasFile && hasMarker) fs.removeSync(file);
});
});
}
findOutputApks (build_type, arch) {
return findOutputApksHelper(this.apkDir, build_type, arch).sort(apkSorter);
return findOutputFilesHelper(this.apkDir, build_type, arch, 'apk').sort(fileSorter);
}
findOutputBundles (build_type) {
return findOutputBundlesHelper(this.aabDir, build_type);
return findOutputFilesHelper(this.aabDir, build_type, false, 'aab').sort(fileSorter);
}
fetchBuildResults (build_type, arch) {
@@ -318,95 +378,3 @@ class ProjectBuilder {
}
module.exports = ProjectBuilder;
const apkSorter = compareFunc([
// Sort arch specific builds after generic ones
apkPath => /-x86|-arm/.test(apkPath),
// Sort unsigned builds after signed ones
apkPath => /-unsigned/.test(apkPath),
// Sort by file modification time, latest first
apkPath => -fs.statSync(apkPath).mtime.getTime(),
// Sort by file name length, ascending
'length'
]);
function findOutputApksHelper (dir, build_type, arch) {
var shellSilent = shell.config.silent;
shell.config.silent = true;
// list directory recursively
var ret = shell.ls('-R', dir).map(function (file) {
// ls does not include base directory
return path.join(dir, file);
}).filter(function (file) {
// find all APKs
return file.match(/\.apk?$/i);
}).filter(function (candidate) {
var apkName = path.basename(candidate);
// Need to choose between release and debug .apk.
if (build_type === 'debug') {
return /-debug/.exec(apkName) && !/-unaligned|-unsigned/.exec(apkName);
}
if (build_type === 'release') {
return /-release/.exec(apkName) && !/-unaligned/.exec(apkName);
}
return true;
}).sort(apkSorter);
shellSilent = shellSilent;
if (ret.length === 0) {
return ret;
}
// Assume arch-specific build if newest apk has -x86 or -arm.
var archSpecific = !!/-x86|-arm/.exec(path.basename(ret[0]));
// And show only arch-specific ones (or non-arch-specific)
ret = ret.filter(function (p) {
return !!/-x86|-arm/.exec(path.basename(p)) === archSpecific;
});
if (archSpecific && ret.length > 1 && arch) {
ret = ret.filter(function (p) {
return path.basename(p).indexOf('-' + arch) !== -1;
});
}
return ret;
}
// This method was a copy of findOutputApksHelper and modified to look for bundles
// While replacing shell with fs-extra, it might be a good idea to see if we can
// generalise these findOutput methods.
function findOutputBundlesHelper (dir, build_type) {
// This is an unused variable that was copied from findOutputApksHelper
// we are pretty sure it was meant to reset shell.config.silent back to
// the original value. However shell is planned to be replaced,
// it was left as is to avoid unintended consequences.
const shellSilent = shell.config.silent;
shell.config.silent = true;
// list directory recursively
const ret = shell.ls('-R', dir).map(function (file) {
return path.join(dir, file); // ls does not include base directory
}).filter(function (file) {
return file.match(/\.aab?$/i); // find all bundles
}).filter(function (candidate) {
// Need to choose between release and debug bundle.
if (build_type === 'debug') {
return /debug/.exec(candidate);
}
if (build_type === 'release') {
return /release/.exec(candidate);
}
return true;
});
return ret;
}
function isAutoGenerated (file) {
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
}

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -19,24 +17,37 @@
under the License.
*/
var shelljs = require('shelljs');
var child_process = require('child_process');
var Q = require('q');
const execa = require('execa');
var path = require('path');
var fs = require('fs');
var fs = require('fs-extra');
var os = require('os');
var which = require('which');
var REPO_ROOT = path.join(__dirname, '..', '..', '..', '..');
var PROJECT_ROOT = path.join(__dirname, '..', '..');
var CordovaError = require('cordova-common').CordovaError;
var superspawn = require('cordova-common').superspawn;
const { CordovaError, ConfigParser, events } = require('cordova-common');
var android_sdk = require('./android_sdk');
const { createEditor } = require('properties-parser');
function forgivingWhichSync (cmd) {
try {
return fs.realpathSync(shelljs.which(cmd));
} catch (e) {
return '';
const whichResult = which.sync(cmd, { nothrow: true });
// On null, returns empty string to maintain backwards compatibility
// realpathSync follows symlinks
return whichResult === null ? '' : fs.realpathSync(whichResult);
}
function getJDKDirectory (directory) {
const p = path.resolve(directory, 'java');
if (fs.existsSync(p)) {
const directories = fs.readdirSync(p);
for (let i = 0; i < directories.length; i++) {
const dir = directories[i];
if (/^(jdk)+./.test(dir)) {
return path.resolve(directory, 'java', dir);
}
}
}
return null;
}
module.exports.isWindows = function () {
@@ -47,31 +58,49 @@ module.exports.isDarwin = function () {
return (os.platform() === 'darwin');
};
// Get valid target from framework/project.properties if run from this repo
// Otherwise get target from project.properties file within a generated cordova-android project
/**
* @description Get valid target from framework/project.properties if run from this repo
* Otherwise get target from project.properties file within a generated cordova-android project
* @returns {string} The android target in format "android-${target}"
*/
module.exports.get_target = function () {
function extractFromFile (filePath) {
var target = shelljs.grep(/\btarget=/, filePath);
if (!target) {
throw new Error('Could not find android target within: ' + filePath);
const projectPropertiesPaths = [
path.join(REPO_ROOT, 'framework', 'project.properties'),
path.join(PROJECT_ROOT, 'project.properties')
];
// Get the minimum required target API from the framework.
let target = projectPropertiesPaths.filter(filePath => fs.existsSync(filePath))
.map(filePath => createEditor(filePath).get('target'))
.pop();
if (!target) {
throw new Error(`We could not locate the target from the "project.properties" at either "${projectPropertiesPaths.join('", "')}".`);
}
// If the repo config.xml file exists, find the desired targetSdkVersion.
const configFile = path.join(REPO_ROOT, 'config.xml');
if (!fs.existsSync(configFile)) return target;
const configParser = new ConfigParser(configFile);
const desiredAPI = parseInt(configParser.getPreference('android-targetSdkVersion', 'android'), 10);
if (!isNaN(desiredAPI)) {
const minimumAPI = parseInt(target.split('-').pop(), 10);
if (desiredAPI >= minimumAPI) {
target = `android-${desiredAPI}`;
} else {
events.emit('warn', `android-targetSdkVersion should be greater than or equal to ${minimumAPI}.`);
}
return target.split('=')[1].trim();
}
var repo_file = path.join(REPO_ROOT, 'framework', 'project.properties');
if (fs.existsSync(repo_file)) {
return extractFromFile(repo_file);
}
var project_file = path.join(PROJECT_ROOT, 'project.properties');
if (fs.existsSync(project_file)) {
// if no target found, we're probably in a project and project.properties is in PROJECT_ROOT.
return extractFromFile(project_file);
}
throw new Error('Could not find android target in either ' + repo_file + ' nor ' + project_file);
return target;
};
// Returns a promise. Called only by build and clean commands.
module.exports.check_ant = function () {
return superspawn.spawn('ant', ['-version']).then(function (output) {
return execa('ant', ['-version']).then(({ stdout: output }) => {
// Parse Ant version from command output
return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1];
}).catch(function (err) {
@@ -88,19 +117,18 @@ module.exports.get_gradle_wrapper = function () {
var program_dir;
// OK, This hack only works on Windows, not on Mac OS or Linux. We will be deleting this eventually!
if (module.exports.isWindows()) {
var result = child_process.spawnSync(path.join(__dirname, 'getASPath.bat'));
var result = execa.sync(path.join(__dirname, 'getASPath.bat'));
// console.log('result.stdout =' + result.stdout.toString());
// console.log('result.stderr =' + result.stderr.toString());
if (result.stderr.toString().length > 0) {
var androidPath = path.join(process.env['ProgramFiles'], 'Android') + '/';
var androidPath = path.join(process.env.ProgramFiles, 'Android') + '/';
if (fs.existsSync(androidPath)) {
program_dir = fs.readdirSync(androidPath);
while (i < program_dir.length && !foundStudio) {
if (program_dir[i].startsWith('Android Studio')) {
foundStudio = true;
androidStudioPath = path.join(process.env['ProgramFiles'], 'Android', program_dir[i], 'gradle');
androidStudioPath = path.join(process.env.ProgramFiles, 'Android', program_dir[i], 'gradle');
} else { ++i; }
}
}
@@ -125,31 +153,30 @@ 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_HOME'];
var d = Q.defer();
var sdkDir = process.env.ANDROID_SDK_ROOT || process.env.ANDROID_HOME;
if (!sdkDir) {
return Q.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Could not find Android SDK directory.\n' +
'Might need to install Android SDK or set up \'ANDROID_HOME\' env variable.'));
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();
if (gradlePath.length !== 0) { d.resolve(gradlePath); } else {
d.reject(new CordovaError('Could not find an installed version of Gradle either in Android Studio,\n' +
'or on your system to install the gradle wrapper. Please include gradle \n' +
'in your path, or install Android Studio'));
}
return d.promise;
if (gradlePath.length !== 0) return Promise.resolve(gradlePath);
return Promise.reject(new CordovaError('Could not find an installed version of Gradle either in Android Studio,\n' +
'or on your system to install the gradle wrapper. Please include gradle \n' +
'in your path, or install Android Studio'));
};
// Returns a promise.
module.exports.check_java = function () {
var javacPath = forgivingWhichSync('javac');
var hasJavaHome = !!process.env['JAVA_HOME'];
return Q().then(function () {
var hasJavaHome = !!process.env.JAVA_HOME;
return Promise.resolve().then(function () {
if (hasJavaHome) {
// Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh).
if (!javacPath) {
process.env['PATH'] += path.delimiter + path.join(process.env['JAVA_HOME'], 'bin');
process.env.PATH += path.delimiter + path.join(process.env.JAVA_HOME, 'bin');
}
} else {
if (javacPath) {
@@ -157,8 +184,8 @@ module.exports.check_java = function () {
var find_java = '/usr/libexec/java_home';
var default_java_error_msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting it manually.';
if (fs.existsSync(find_java)) {
return superspawn.spawn(find_java).then(function (stdout) {
process.env['JAVA_HOME'] = stdout.trim();
return execa(find_java).then(({ stdout }) => {
process.env.JAVA_HOME = stdout;
}).catch(function (err) {
if (err) {
throw new CordovaError(default_java_error_msg);
@@ -166,39 +193,36 @@ module.exports.check_java = function () {
});
} else {
// See if we can derive it from javac's location.
// fs.realpathSync is require on Ubuntu, which symplinks from /usr/bin -> JDK
var maybeJavaHome = path.dirname(path.dirname(javacPath));
if (fs.existsSync(path.join(maybeJavaHome, 'lib', 'tools.jar'))) {
process.env['JAVA_HOME'] = maybeJavaHome;
process.env.JAVA_HOME = maybeJavaHome;
} else {
throw new CordovaError(default_java_error_msg);
}
}
} else if (module.exports.isWindows()) {
// Try to auto-detect java in the default install paths.
var oldSilent = shelljs.config.silent;
shelljs.config.silent = true;
var firstJdkDir =
shelljs.ls(process.env['ProgramFiles'] + '\\java\\jdk*')[0] ||
shelljs.ls('C:\\Program Files\\java\\jdk*')[0] ||
shelljs.ls('C:\\Program Files (x86)\\java\\jdk*')[0];
shelljs.config.silent = oldSilent;
const programFilesEnv = path.resolve(process.env.ProgramFiles);
const programFiles = 'C:\\Program Files\\';
const programFilesx86 = 'C:\\Program Files (x86)\\';
let firstJdkDir =
getJDKDirectory(programFilesEnv) ||
getJDKDirectory(programFiles) ||
getJDKDirectory(programFilesx86);
if (firstJdkDir) {
// shelljs always uses / in paths.
firstJdkDir = firstJdkDir.replace(/\//g, path.sep);
if (!javacPath) {
process.env['PATH'] += path.delimiter + path.join(firstJdkDir, 'bin');
process.env.PATH += path.delimiter + path.join(firstJdkDir, 'bin');
}
process.env['JAVA_HOME'] = firstJdkDir;
process.env.JAVA_HOME = firstJdkDir;
}
}
}
}).then(function () {
return Q.denodeify(child_process.exec)('javac -version')
.then(outputs => {
// outputs contains two entries: stdout and stderr
return execa('javac', ['-version'], { all: true })
.then(({ all: output }) => {
// Java <= 8 writes version info to stderr, Java >= 9 to stdout
const output = outputs.join('').trim();
const match = /javac\s+([\d.]+)/i.exec(output);
return match && match[1];
}, () => {
@@ -206,9 +230,9 @@ module.exports.check_java = function () {
'Failed to run "javac -version", make sure that you have a JDK version 8 installed.\n' +
'You can get it from the following location:\n' +
'https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html';
if (process.env['JAVA_HOME']) {
if (process.env.JAVA_HOME) {
msg += '\n\n';
msg += 'Your JAVA_HOME is invalid: ' + process.env['JAVA_HOME'];
msg += 'Your JAVA_HOME is invalid: ' + process.env.JAVA_HOME;
}
throw new CordovaError(msg);
});
@@ -217,55 +241,74 @@ module.exports.check_java = function () {
// Returns a promise.
module.exports.check_android = function () {
return Q().then(function () {
var androidCmdPath = forgivingWhichSync('android');
var adbInPath = forgivingWhichSync('adb');
var avdmanagerInPath = forgivingWhichSync('avdmanager');
var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
return Promise.resolve().then(function () {
function maybeSetAndroidHome (value) {
if (!hasAndroidHome && fs.existsSync(value)) {
hasAndroidHome = true;
process.env['ANDROID_HOME'] = value;
process.env.ANDROID_SDK_ROOT = value;
}
}
var androidCmdPath = forgivingWhichSync('android');
var adbInPath = forgivingWhichSync('adb');
var avdmanagerInPath = forgivingWhichSync('avdmanager');
var hasAndroidHome = false;
if (process.env.ANDROID_SDK_ROOT) {
maybeSetAndroidHome(path.resolve(process.env.ANDROID_SDK_ROOT));
}
// First ensure ANDROID_HOME is set
// If we have no hints (nothing in PATH), try a few default locations
if (!hasAndroidHome && !androidCmdPath && !adbInPath && !avdmanagerInPath) {
if (process.env['ANDROID_SDK_ROOT']) {
// Quick fix to set ANDROID_HOME according to ANDROID_SDK_ROOT
// if ANDROID_HOME is **not** defined and
// ANDROID_SDK_ROOT **is** defined
// according to environment variables as documented in:
// https://developer.android.com/studio/command-line/variables
maybeSetAndroidHome(path.join(process.env['ANDROID_SDK_ROOT']));
if (process.env.ANDROID_HOME) {
// Fallback to deprecated `ANDROID_HOME` variable
maybeSetAndroidHome(path.join(process.env.ANDROID_HOME));
}
if (module.exports.isWindows()) {
// Android Studio 1.0 installer
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'sdk'));
maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'sdk'));
if (process.env.LOCALAPPDATA) {
maybeSetAndroidHome(path.join(process.env.LOCALAPPDATA, 'Android', 'sdk'));
}
if (process.env.ProgramFiles) {
maybeSetAndroidHome(path.join(process.env.ProgramFiles, 'Android', 'sdk'));
}
// Android Studio pre-1.0 installer
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'android-studio', 'sdk'));
maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'android-studio', 'sdk'));
if (process.env.LOCALAPPDATA) {
maybeSetAndroidHome(path.join(process.env.LOCALAPPDATA, 'Android', 'android-studio', 'sdk'));
}
if (process.env.ProgramFiles) {
maybeSetAndroidHome(path.join(process.env.ProgramFiles, 'Android', 'android-studio', 'sdk'));
}
// Stand-alone installer
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'android-sdk'));
maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'android-sdk'));
if (process.env.LOCALAPPDATA) {
maybeSetAndroidHome(path.join(process.env.LOCALAPPDATA, 'Android', 'android-sdk'));
}
if (process.env.ProgramFiles) {
maybeSetAndroidHome(path.join(process.env.ProgramFiles, 'Android', 'android-sdk'));
}
} else if (module.exports.isDarwin()) {
// Android Studio 1.0 installer
maybeSetAndroidHome(path.join(process.env['HOME'], 'Library', 'Android', 'sdk'));
if (process.env.HOME) {
maybeSetAndroidHome(path.join(process.env.HOME, 'Library', 'Android', 'sdk'));
}
// Android Studio pre-1.0 installer
maybeSetAndroidHome('/Applications/Android Studio.app/sdk');
// Stand-alone zip file that user might think to put under /Applications
maybeSetAndroidHome('/Applications/android-sdk-macosx');
maybeSetAndroidHome('/Applications/android-sdk');
}
if (process.env['HOME']) {
if (process.env.HOME) {
// Stand-alone zip file that user might think to put under their home directory
maybeSetAndroidHome(path.join(process.env['HOME'], 'android-sdk-macosx'));
maybeSetAndroidHome(path.join(process.env['HOME'], 'android-sdk'));
maybeSetAndroidHome(path.join(process.env.HOME, 'android-sdk-macosx'));
maybeSetAndroidHome(path.join(process.env.HOME, 'android-sdk'));
}
}
if (!hasAndroidHome) {
// If we dont have ANDROID_HOME, but we do have some tools on the PATH, try to infer from the tooling PATH.
// 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 (androidCmdPath) {
parentDir = path.dirname(androidCmdPath);
@@ -273,7 +316,7 @@ module.exports.check_android = function () {
if (path.basename(parentDir) === 'tools' || fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) {
maybeSetAndroidHome(grandParentDir);
} else {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' environment variable. Try setting it manually.\n' +
'Detected \'android\' command at ' + parentDir + ' but no \'tools\' directory found near.\n' +
'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'tools directory.');
}
@@ -284,7 +327,7 @@ module.exports.check_android = function () {
if (path.basename(parentDir) === 'platform-tools') {
maybeSetAndroidHome(grandParentDir);
} else {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' environment variable. Try setting it manually.\n' +
'Detected \'adb\' command at ' + parentDir + ' but no \'platform-tools\' directory found near.\n' +
'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'platform-tools directory.');
}
@@ -295,29 +338,29 @@ module.exports.check_android = function () {
if (path.basename(parentDir) === 'bin' && path.basename(grandParentDir) === 'tools') {
maybeSetAndroidHome(path.dirname(grandParentDir));
} else {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' environment variable. Try setting it manually.\n' +
'Detected \'avdmanager\' command at ' + parentDir + ' but no \'tools' + path.sep + 'bin\' directory found near.\n' +
'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'tools' + path.sep + 'bin directory.');
}
}
}
if (!process.env['ANDROID_HOME']) {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
if (!process.env.ANDROID_SDK_ROOT) {
throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' environment variable. Try setting it manually.\n' +
'Failed to find \'android\' command in your \'PATH\'. Try update your \'PATH\' to include path to valid SDK directory.');
}
if (!fs.existsSync(process.env['ANDROID_HOME'])) {
throw new CordovaError('\'ANDROID_HOME\' environment variable is set to non-existent path: ' + process.env['ANDROID_HOME'] +
if (!fs.existsSync(process.env.ANDROID_SDK_ROOT)) {
throw new CordovaError('\'ANDROID_SDK_ROOT\' environment variable is set to non-existent path: ' + process.env.ANDROID_SDK_ROOT +
'\nTry update it manually to point to valid SDK directory.');
}
// Next let's make sure relevant parts of the SDK tooling is in our PATH
if (hasAndroidHome && !androidCmdPath) {
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools');
process.env.PATH += path.delimiter + path.join(process.env.ANDROID_SDK_ROOT, 'tools');
}
if (hasAndroidHome && !adbInPath) {
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'platform-tools');
process.env.PATH += path.delimiter + path.join(process.env.ANDROID_SDK_ROOT, 'platform-tools');
}
if (hasAndroidHome && !avdmanagerInPath) {
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools', 'bin');
process.env.PATH += path.delimiter + path.join(process.env.ANDROID_SDK_ROOT, 'tools', 'bin');
}
return hasAndroidHome;
});
@@ -362,10 +405,12 @@ module.exports.check_android_target = function (originalError) {
// Returns a promise.
module.exports.run = function () {
return Q.all([this.check_java(), this.check_android()]).then(function (values) {
console.log('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('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)');
return Promise.all([this.check_java(), this.check_android()]).then(function (values) {
console.log('Using Android SDK: ' + process.env.ANDROID_SDK_ROOT);
if (!String(values[0]).startsWith('1.8.')) {
throw new CordovaError(
@@ -404,7 +449,6 @@ 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 () {
var requirements = [
new Requirement('java', 'Java JDK'),
new Requirement('androidSdk', 'Android SDK'),
@@ -429,7 +473,7 @@ module.exports.check_all = function () {
}, function (err) {
requirement.metadata.reason = err instanceof Error ? err.message : err;
});
}, Q()).then(function () {
}, Promise.resolve()).then(function () {
// When chain is completed, return requirements array to upstream API
return requirements;
});

View File

@@ -17,10 +17,10 @@
under the License.
*/
let fs = require('fs');
let path = require('path');
let propertiesParser = require('properties-parser');
let events = require('cordova-common').events;
const fs = require('fs');
const path = require('path');
const propertiesParser = require('properties-parser');
const events = require('cordova-common').events;
class GradlePropertiesParser {
/**
@@ -36,8 +36,9 @@ class GradlePropertiesParser {
// to allow dex in process
'org.gradle.jvmargs': '-Xmx2048m',
// allow NDK to be used - required by Gradle 1.5 plugin
'android.useDeprecatedNdk': 'true'
// Android X
'android.useAndroidX': 'false',
'android.enableJetifier': 'false'
// Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
// 'org.gradle.parallel': 'true'
@@ -81,7 +82,7 @@ class GradlePropertiesParser {
_configureProperties (properties) {
// Iterate though the properties and set only if missing.
Object.keys(properties).forEach(key => {
let value = this.gradleFile.get(key);
const value = this.gradleFile.get(key);
if (!value) {
// Handles the case of adding missing defaults or new properties that are missing.
@@ -89,8 +90,15 @@ class GradlePropertiesParser {
this.gradleFile.set(key, properties[key]);
} else if (value !== properties[key]) {
if (this._defaults[key] && this._defaults[key] !== properties[key]) {
// Since the value does not match default, we will notify the discrepancy with Cordova's recommended value.
events.emit('info', `[Gradle Properties] Detected Gradle property "${key}" with the value of "${properties[key]}", Cordova's recommended value is "${this._defaults[key]}"`);
let shouldEmit = true;
if (key === 'org.gradle.jvmargs') {
shouldEmit = this._isJVMMemoryLessThanRecommended(properties[key], this._defaults[key]);
}
if (shouldEmit) {
// Since the value does not match default, we will notify the discrepancy with Cordova's recommended value.
events.emit('info', `[Gradle Properties] Detected Gradle property "${key}" with the value of "${properties[key]}", Cordova's recommended value is "${this._defaults[key]}"`);
}
} else {
// When the current value exists but does not match the new value or does matches the default key value, the new value it set.
events.emit('verbose', `[Gradle Properties] Updating Gradle property "${key}" with the value of "${properties[key]}"`);
@@ -102,6 +110,34 @@ class GradlePropertiesParser {
});
}
_isJVMMemoryLessThanRecommended (memoryValue, recommendedMemoryValue) {
const UNIT = 2;
const SIZE = 1;
const regex = /-Xmx+([0-9]+)+([mMgGkK])/;
const recommendedCapture = regex.exec(recommendedMemoryValue);
const recommendedBase = this._getBaseJVMSize(recommendedCapture[SIZE], recommendedCapture[UNIT]);
const memoryCapture = regex.exec(memoryValue);
const memoryBase = this._getBaseJVMSize(memoryCapture[SIZE], memoryCapture[UNIT]);
return memoryBase < recommendedBase;
}
_getBaseJVMSize (size, unit) {
const KILOBYTE = 1024;
const MEGABYTE = 1048576;
const GIGABYTE = 1073741824;
switch (unit.toLowerCase()) {
case 'k': return size * KILOBYTE;
case 'm': return size * MEGABYTE;
case 'g': return size * GIGABYTE;
}
events.emit('warn', `[Gradle Properties] Unknown memory size unit (${unit})`);
return null;
}
/**
* Saves any changes that has been made to the properties file.
*/

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -19,11 +17,11 @@
under the License.
*/
const execa = require('execa');
var build = require('./build');
var path = require('path');
var Adb = require('./Adb');
var AndroidManifest = require('./AndroidManifest');
var spawn = require('cordova-common').superspawn.spawn;
var CordovaError = require('cordova-common').CordovaError;
var events = require('cordova-common').events;
@@ -37,7 +35,7 @@ module.exports.list = function (lookHarder) {
// adb kill-server doesn't seem to do the trick.
// Could probably find a x-platform version of killall, but I'm not actually
// sure that this scenario even happens on non-OSX machines.
return spawn('killall', ['adb']).then(function () {
return execa('killall', ['adb']).then(function () {
events.emit('verbose', 'Restarting adb to see if more devices are detected.');
return Adb.devices();
}, function () {

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -19,6 +17,8 @@
under the License.
*/
const execa = require('execa');
const fs = require('fs-extra');
var android_versions = require('android-versions');
var retry = require('./retry');
var build = require('./build');
@@ -26,53 +26,49 @@ var path = require('path');
var Adb = require('./Adb');
var AndroidManifest = require('./AndroidManifest');
var events = require('cordova-common').events;
var superspawn = require('cordova-common').superspawn;
var CordovaError = require('cordova-common').CordovaError;
var shelljs = require('shelljs');
var android_sdk = require('./android_sdk');
var check_reqs = require('./check_reqs');
var which = require('which');
var os = require('os');
var fs = require('fs');
var child_process = require('child_process');
// constants
var ONE_SECOND = 1000; // in milliseconds
var ONE_MINUTE = 60 * ONE_SECOND; // in milliseconds
var INSTALL_COMMAND_TIMEOUT = 5 * ONE_MINUTE; // in milliseconds
var NUM_INSTALL_RETRIES = 3;
var CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds
var EXEC_KILL_SIGNAL = 'SIGKILL';
const ONE_SECOND = 1000; // in milliseconds
const ONE_MINUTE = 60 * ONE_SECOND; // in milliseconds
const INSTALL_COMMAND_TIMEOUT = 5 * ONE_MINUTE; // in milliseconds
const NUM_INSTALL_RETRIES = 3;
const CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds
const EXEC_KILL_SIGNAL = 'SIGKILL';
function forgivingWhichSync (cmd) {
try {
return fs.realpathSync(shelljs.which(cmd));
} catch (e) {
return '';
}
const whichResult = which.sync(cmd, { nothrow: true });
// On null, returns empty string to maintain backwards compatibility
// realpathSync follows symlinks
return whichResult === null ? '' : fs.realpathSync(whichResult);
}
module.exports.list_images_using_avdmanager = function () {
return superspawn.spawn('avdmanager', ['list', 'avd']).then(function (output) {
return execa('avdmanager', ['list', 'avd']).then(({ stdout: output }) => {
var response = output.split('\n');
var emulator_list = [];
for (var i = 1; i < response.length; i++) {
// To return more detailed information use img_obj
var img_obj = {};
if (response[i].match(/Name:\s/)) {
img_obj['name'] = response[i].split('Name: ')[1].replace('\r', '');
img_obj.name = response[i].split('Name: ')[1].replace('\r', '');
if (response[i + 1].match(/Device:\s/)) {
i++;
img_obj['device'] = response[i].split('Device: ')[1].replace('\r', '');
img_obj.device = response[i].split('Device: ')[1].replace('\r', '');
}
if (response[i + 1].match(/Path:\s/)) {
i++;
img_obj['path'] = response[i].split('Path: ')[1].replace('\r', '');
img_obj.path = response[i].split('Path: ')[1].replace('\r', '');
}
if (response[i + 1].match(/Target:\s/)) {
i++;
if (response[i + 1].match(/ABI:\s/)) {
img_obj['abi'] = response[i + 1].split('ABI: ')[1].replace('\r', '');
img_obj.abi = response[i + 1].split('ABI: ')[1].replace('\r', '');
}
// This next conditional just aims to match the old output of `android list avd`
// We do so so that we don't have to change the logic when parsing for the
@@ -80,24 +76,24 @@ module.exports.list_images_using_avdmanager = function () {
// This allows us to transitionally support both `android` and `avdmanager` binaries,
// depending on what SDK version the user has
if (response[i + 1].match(/Based\son:\s/)) {
img_obj['target'] = response[i + 1].split('Based on:')[1];
if (img_obj['target'].match(/Tag\/ABI:\s/)) {
img_obj['target'] = img_obj['target'].split('Tag/ABI:')[0].replace('\r', '').trim();
if (img_obj['target'].indexOf('(') > -1) {
img_obj['target'] = img_obj['target'].substr(0, img_obj['target'].indexOf('(') - 1).trim();
img_obj.target = response[i + 1].split('Based on:')[1];
if (img_obj.target.match(/Tag\/ABI:\s/)) {
img_obj.target = img_obj.target.split('Tag/ABI:')[0].replace('\r', '').trim();
if (img_obj.target.indexOf('(') > -1) {
img_obj.target = img_obj.target.substr(0, img_obj.target.indexOf('(') - 1).trim();
}
}
var version_string = img_obj['target'].replace(/Android\s+/, '');
var version_string = img_obj.target.replace(/Android\s+/, '');
var api_level = android_sdk.version_string_to_api_level[version_string];
if (api_level) {
img_obj['target'] += ' (API level ' + api_level + ')';
img_obj.target += ' (API level ' + api_level + ')';
}
}
}
if (response[i + 1].match(/Skin:\s/)) {
i++;
img_obj['skin'] = response[i].split('Skin: ')[1].replace('\r', '');
img_obj.skin = response[i].split('Skin: ')[1].replace('\r', '');
}
emulator_list.push(img_obj);
@@ -106,41 +102,40 @@ module.exports.list_images_using_avdmanager = function () {
if (response[i].match(/Name:\s/)) {
emulator_list.push(response[i].split('Name: ')[1].replace('\r', '');
} */
}
return emulator_list;
});
};
module.exports.list_images_using_android = function () {
return superspawn.spawn('android', ['list', 'avd']).then(function (output) {
return execa('android', ['list', 'avd']).then(({ stdout: output }) => {
var response = output.split('\n');
var emulator_list = [];
for (var i = 1; i < response.length; i++) {
// To return more detailed information use img_obj
var img_obj = {};
if (response[i].match(/Name:\s/)) {
img_obj['name'] = response[i].split('Name: ')[1].replace('\r', '');
img_obj.name = response[i].split('Name: ')[1].replace('\r', '');
if (response[i + 1].match(/Device:\s/)) {
i++;
img_obj['device'] = response[i].split('Device: ')[1].replace('\r', '');
img_obj.device = response[i].split('Device: ')[1].replace('\r', '');
}
if (response[i + 1].match(/Path:\s/)) {
i++;
img_obj['path'] = response[i].split('Path: ')[1].replace('\r', '');
img_obj.path = response[i].split('Path: ')[1].replace('\r', '');
}
if (response[i + 1].match(/\(API\slevel\s/) || (response[i + 2] && response[i + 2].match(/\(API\slevel\s/))) {
i++;
var secondLine = response[i + 1].match(/\(API\slevel\s/) ? response[i + 1] : '';
img_obj['target'] = (response[i] + secondLine).split('Target: ')[1].replace('\r', '');
img_obj.target = (response[i] + secondLine).split('Target: ')[1].replace('\r', '');
}
if (response[i + 1].match(/ABI:\s/)) {
i++;
img_obj['abi'] = response[i].split('ABI: ')[1].replace('\r', '');
img_obj.abi = response[i].split('ABI: ')[1].replace('\r', '');
}
if (response[i + 1].match(/Skin:\s/)) {
i++;
img_obj['skin'] = response[i].split('Skin: ')[1].replace('\r', '');
img_obj.skin = response[i].split('Skin: ')[1].replace('\r', '');
}
emulator_list.push(img_obj);
@@ -149,7 +144,6 @@ module.exports.list_images_using_android = function () {
if (response[i].match(/Name:\s/)) {
emulator_list.push(response[i].split('Name: ')[1].replace('\r', '');
} */
}
return emulator_list;
});
@@ -229,7 +223,7 @@ module.exports.list_started = function () {
// Returns a promise.
// TODO: we should remove this, there's a more robust method under android_sdk.js
module.exports.list_targets = function () {
return superspawn.spawn('android', ['list', 'targets'], { cwd: os.tmpdir() }).then(function (output) {
return execa('android', ['list', 'targets'], { cwd: os.tmpdir() }).then(({ stdout: output }) => {
var target_out = output.split('\n');
var targets = [];
for (var i = target_out.length; i >= 0; i--) {
@@ -291,11 +285,10 @@ module.exports.start = function (emulator_ID, 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(shelljs.which('emulator'));
var emulator_dir = path.dirname(which.sync('emulator'));
var args = ['-avd', emulatorId, '-port', port];
// Don't wait for it to finish, since the emulator will probably keep running for a long time.
child_process
.spawn('emulator', args, { stdio: 'inherit', detached: true, cwd: emulator_dir })
execa('emulator', args, { stdio: 'inherit', detached: true, cwd: emulator_dir })
.unref();
// wait for emulator to start
@@ -359,8 +352,8 @@ module.exports.wait_for_emulator = function (port) {
*/
module.exports.wait_for_boot = function (emulator_id, time_remaining) {
var self = this;
return Adb.shell(emulator_id, 'ps').then(function (output) {
if (output.match(/android\.process\.acore/)) {
return Adb.shell(emulator_id, 'getprop sys.boot_completed').then(function (output) {
if (output.match(/1/)) {
return true;
} else if (time_remaining === 0) {
return false;
@@ -387,22 +380,22 @@ module.exports.wait_for_boot = function (emulator_id, time_remaining) {
module.exports.create_image = function (name, target) {
console.log('Creating new avd named ' + name);
if (target) {
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', target]).then(null, function (error) {
return execa('android', ['create', 'avd', '--name', name, '--target', target]).then(null, function (error) {
console.error('ERROR : Failed to create emulator image : ');
console.error(' Do you have the latest android targets including ' + target + '?');
console.error(error);
console.error(error.message);
});
} else {
console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.');
// TODO: there's a more robust method for finding targets in android_sdk.js
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]]).then(function () {
return execa('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]]).then(function () {
// TODO: This seems like another error case, even though it always happens.
console.error('ERROR : Unable to create an avd emulator, no targets found.');
console.error('Ensure you have targets available by running the "android" command');
return Promise.reject(new CordovaError());
}, function (error) {
console.error('ERROR : Failed to create emulator image : ');
console.error(error);
console.error(error.message);
});
}
};
@@ -432,7 +425,6 @@ module.exports.resolveTarget = function (target) {
* Returns a promise.
*/
module.exports.install = function (givenTarget, buildResults) {
var target;
// We need to find the proper path to the Android Manifest
const manifestPath = path.join(__dirname, '..', '..', 'app', 'src', 'main', 'AndroidManifest.xml');
@@ -456,7 +448,6 @@ module.exports.install = function (givenTarget, buildResults) {
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app
// or the app doesn't installed at all, so no error catching needed.
return Promise.resolve().then(function () {
var apk_path = build.findBestApkForArchitecture(buildResults, target.arch);
var execOptions = {
cwd: os.tmpdir(),
@@ -474,24 +465,21 @@ module.exports.install = function (givenTarget, buildResults) {
function adbInstallWithOptions (target, apk, opts) {
events.emit('verbose', 'Installing apk ' + apk + ' on ' + target + '...');
var command = 'adb -s ' + target + ' install -r "' + apk + '"';
return new Promise(function (resolve, reject) {
child_process.exec(command, opts, function (err, stdout, stderr) {
if (err) reject(new CordovaError('Error executing "' + command + '": ' + stderr));
// adb does not return an error code even if installation fails. Instead it puts a specific
// message to stdout, so we have to use RegExp matching to detect installation failure.
else if (/Failure/.test(stdout)) {
if (stdout.match(/INSTALL_PARSE_FAILED_NO_CERTIFICATES/)) {
stdout += 'Sign the build using \'-- --keystore\' or \'--buildConfig\'' +
' or sign and deploy the unsigned apk manually using Android tools.';
} else if (stdout.match(/INSTALL_FAILED_VERSION_DOWNGRADE/)) {
stdout += 'You\'re trying to install apk with a lower versionCode that is already installed.' +
'\nEither uninstall an app or increment the versionCode.';
}
const args = ['-s', target, 'install', '-r', apk];
return execa('adb', args, opts).then(({ stdout }) => {
// adb does not return an error code even if installation fails. Instead it puts a specific
// message to stdout, so we have to use RegExp matching to detect installation failure.
if (/Failure/.test(stdout)) {
if (stdout.match(/INSTALL_PARSE_FAILED_NO_CERTIFICATES/)) {
stdout += 'Sign the build using \'-- --keystore\' or \'--buildConfig\'' +
' or sign and deploy the unsigned apk manually using Android tools.';
} else if (stdout.match(/INSTALL_FAILED_VERSION_DOWNGRADE/)) {
stdout += 'You\'re trying to install apk with a lower versionCode that is already installed.' +
'\nEither uninstall an app or increment the versionCode.';
}
reject(new CordovaError('Failed to install apk to emulator: ' + stdout));
} else resolve(stdout);
});
throw new CordovaError('Failed to install apk to emulator: ' + stdout);
}
});
}
@@ -518,7 +506,6 @@ module.exports.install = function (givenTarget, buildResults) {
});
// unlock screen
}).then(function () {
events.emit('verbose', 'Unlocking screen...');
return Adb.shell(target.target, 'input keyevent 82');
}).then(function () {

View File

@@ -5,9 +5,9 @@
:: 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

View File

@@ -5,9 +5,9 @@
:: 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

View File

@@ -5,9 +5,9 @@
:: 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

View File

@@ -5,9 +5,9 @@
:: 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
@@ -20,7 +20,7 @@ SET script_path="%~dp0list-emulator-images"
IF EXIST %script_path% (
node %script_path% %*
) ELSE (
ECHO.
ECHO.
ECHO ERROR: Could not find 'list-emulator-images' script in 'cordova\lib' folder, aborting...>&2
EXIT /B 1
)

View File

@@ -5,9 +5,9 @@
:: 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

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -21,8 +19,7 @@
var path = require('path');
var os = require('os');
var Q = require('q');
var child_process = require('child_process');
var execa = require('execa');
var ROOT = path.join(__dirname, '..', '..');
/*
@@ -30,8 +27,7 @@ var ROOT = path.join(__dirname, '..', '..');
* Returns a promise.
*/
module.exports.run = function () {
var d = Q.defer();
var adb = child_process.spawn('adb', ['logcat'], { cwd: os.tmpdir() });
var adb = execa('adb', ['logcat'], { cwd: os.tmpdir(), stderr: 'inherit' });
adb.stdout.on('data', function (data) {
var lines = data ? data.toString().split('\n') : [];
@@ -39,14 +35,7 @@ module.exports.run = function () {
console.log(out.join('\n'));
});
adb.stderr.on('data', console.error);
adb.on('close', function (code) {
if (code > 0) {
d.reject('Failed to run logcat command.');
} else d.resolve();
});
return d.promise;
return adb;
};
module.exports.help = function () {

View File

@@ -14,9 +14,8 @@
*
*/
var fs = require('fs');
var fs = require('fs-extra');
var path = require('path');
var shell = require('shelljs');
var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;
@@ -42,7 +41,7 @@ var handlers = {
deleteJava(project.projectDir, dest);
} else {
// Just remove the file, not the whole parent directory
removeFile(project.projectDir, dest);
removeFile(path.resolve(project.projectDir, dest));
}
}
},
@@ -53,7 +52,7 @@ var handlers = {
},
uninstall: function (obj, plugin, project, options) {
var dest = path.join('app/libs', path.basename(obj.src));
removeFile(project.projectDir, dest);
removeFile(path.resolve(project.projectDir, dest));
}
},
'resource-file': {
@@ -63,10 +62,10 @@ var handlers = {
},
uninstall: function (obj, plugin, project, options) {
var dest = path.join('app', 'src', 'main', obj.target);
removeFile(project.projectDir, dest);
removeFile(path.resolve(project.projectDir, dest));
}
},
'framework': {
framework: {
install: function (obj, plugin, project, options) {
var src = obj.src;
if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
@@ -102,7 +101,7 @@ var handlers = {
if (obj.custom) {
var subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
removeFile(project.projectDir, subRelativeDir);
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);
@@ -143,12 +142,12 @@ var handlers = {
if (!target) throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
removeFileF(path.resolve(project.www, target));
removeFileF(path.resolve(project.www, 'plugins', plugin.id));
removeFile(path.resolve(project.www, target));
removeFile(path.resolve(project.www, 'plugins', plugin.id));
if (options && options.usePlatformWww) {
// CB-11022 remove file from both directories if usePlatformWww is specified
removeFileF(path.resolve(project.platformWww, target));
removeFileF(path.resolve(project.platformWww, 'plugins', plugin.id));
removeFile(path.resolve(project.platformWww, target));
removeFile(path.resolve(project.platformWww, 'plugins', plugin.id));
}
}
},
@@ -166,13 +165,13 @@ var handlers = {
scriptContent = 'cordova.define("' + moduleName + '", function(require, exports, module) {\n' + scriptContent + '\n});\n';
var wwwDest = path.resolve(project.www, 'plugins', plugin.id, obj.src);
shell.mkdir('-p', path.dirname(wwwDest));
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);
shell.mkdir('-p', path.dirname(platformWwwDest));
fs.ensureDirSync(path.dirname(platformWwwDest));
fs.writeFileSync(platformWwwDest, scriptContent, 'utf-8');
}
},
@@ -217,14 +216,11 @@ function copyFile (plugin_dir, src, project_dir, dest, link) {
// check that dest path is located in project directory
if (dest.indexOf(project_dir) !== 0) { throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project'); }
shell.mkdir('-p', path.dirname(dest));
fs.ensureDirSync(path.dirname(dest));
if (link) {
symlinkFileOrDirTree(src, dest);
} else if (fs.statSync(src).isDirectory()) {
// XXX shelljs decides to create a directory when -R|-r is used which sucks. http://goo.gl/nbsjq
shell.cp('-Rf', src + '/*', dest);
} else {
shell.cp('-f', src, dest);
fs.copySync(src, dest);
}
}
@@ -238,11 +234,11 @@ function copyNewFile (plugin_dir, src, project_dir, dest, link) {
function symlinkFileOrDirTree (src, dest) {
if (fs.existsSync(dest)) {
shell.rm('-Rf', dest);
fs.removeSync(dest);
}
if (fs.statSync(src).isDirectory()) {
shell.mkdir('-p', dest);
fs.ensureDirSync(path.dirname(dest));
fs.readdirSync(src).forEach(function (entry) {
symlinkFileOrDirTree(path.join(src, entry), path.join(dest, entry));
});
@@ -251,15 +247,8 @@ function symlinkFileOrDirTree (src, dest) {
}
}
// checks if file exists and then deletes. Error if doesn't exist
function removeFile (project_dir, src) {
var file = path.resolve(project_dir, src);
shell.rm('-Rf', file);
}
// deletes file/directory without checking
function removeFileF (file) {
shell.rm('-Rf', file);
function removeFile (file) {
fs.removeSync(file);
}
// Sometimes we want to remove some java, and prune any unnecessary empty directories
@@ -272,7 +261,7 @@ function removeFileAndParents (baseDir, destFile, stopper) {
var file = path.resolve(baseDir, destFile);
if (!fs.existsSync(file)) return;
removeFileF(file);
removeFile(file);
// check if directory is empty
var curDir = path.dirname(file);

View File

@@ -16,12 +16,10 @@
specific language governing permissions and limitations
under the License.
*/
/* eslint no-useless-escape: 0 */
var Q = require('q');
var fs = require('fs');
var fs = require('fs-extra');
var path = require('path');
var shell = require('shelljs');
const nopt = require('nopt');
var events = require('cordova-common').events;
var AndroidManifest = require('./AndroidManifest');
var checkReqs = require('./check_reqs');
@@ -32,12 +30,25 @@ var FileUpdater = require('cordova-common').FileUpdater;
var PlatformJson = require('cordova-common').PlatformJson;
var PlatformMunger = require('cordova-common').ConfigChanges.PlatformMunger;
var PluginInfoProvider = require('cordova-common').PluginInfoProvider;
const utils = require('./utils');
const GradlePropertiesParser = require('./config/GradlePropertiesParser');
function parseArguments (argv) {
return nopt({
// `jvmargs` is a valid option however, we don't actually want to parse it because we want the entire string as is.
// jvmargs: String
}, {}, argv || [], 0);
}
module.exports.prepare = function (cordovaProject, options) {
var 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());
@@ -47,17 +58,30 @@ module.exports.prepare = function (cordovaProject, options) {
const minSdkVersion = this._config.getPreference('android-minSdkVersion', 'android');
const maxSdkVersion = this._config.getPreference('android-maxSdkVersion', 'android');
const targetSdkVersion = this._config.getPreference('android-targetSdkVersion', 'android');
const androidXEnabled = this._config.getPreference('AndroidXEnabled', 'android');
const isGradlePluginKotlinEnabled = this._config.getPreference('GradlePluginKotlinEnabled', 'android');
const gradlePluginKotlinCodeStyle = this._config.getPreference('GradlePluginKotlinCodeStyle', 'android');
let gradlePropertiesUserConfig = {};
const gradlePropertiesUserConfig = {};
if (minSdkVersion) gradlePropertiesUserConfig.cdvMinSdkVersion = minSdkVersion;
if (maxSdkVersion) gradlePropertiesUserConfig.cdvMaxSdkVersion = maxSdkVersion;
if (targetSdkVersion) gradlePropertiesUserConfig.cdvTargetSdkVersion = targetSdkVersion;
if (args.jvmargs) gradlePropertiesUserConfig['org.gradle.jvmargs'] = args.jvmargs;
if (isGradlePluginKotlinEnabled) {
gradlePropertiesUserConfig['kotlin.code.style'] = gradlePluginKotlinCodeStyle || 'official';
}
let gradlePropertiesParser = new GradlePropertiesParser(this.locations.root);
// Both 'useAndroidX' and 'enableJetifier' are linked together.
if (androidXEnabled) {
gradlePropertiesUserConfig['android.useAndroidX'] = androidXEnabled;
gradlePropertiesUserConfig['android.enableJetifier'] = androidXEnabled;
}
const gradlePropertiesParser = new GradlePropertiesParser(this.locations.root);
gradlePropertiesParser.configure(gradlePropertiesUserConfig);
// Update own www dir with project's www assets and plugins' assets and js-files
return Q.when(updateWww(cordovaProject, this.locations)).then(function () {
return Promise.resolve(updateWww(cordovaProject, this.locations)).then(function () {
// update project according to config.xml changes.
return updateProjectAccordingTo(self._config, self.locations);
}).then(function () {
@@ -77,13 +101,13 @@ module.exports.clean = function (options) {
var projectRoot = path.resolve(this.root, '../..');
if ((options && options.noPrepare) || !fs.existsSync(this.locations.configXml) ||
!fs.existsSync(this.locations.configXml)) {
return Q();
return Promise.resolve();
}
var projectConfig = new ConfigParser(this.locations.configXml);
var self = this;
return Q().then(function () {
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));
@@ -110,7 +134,7 @@ function updateConfigFilesFrom (sourceConfig, configMunger, locations) {
// First cleanup current config and merge project's one into own
// Overwrite platform config.xml with defaults.xml.
shell.cp('-f', locations.defaultConfigXml, locations.configXml);
fs.copySync(locations.defaultConfigXml, locations.configXml);
// Then apply config changes from global munge to all config files
// in project (including project's config)
@@ -186,11 +210,11 @@ function updateProjectAccordingTo (platformConfig, locations) {
var strings = xmlHelpers.parseElementtreeSync(locations.strings);
var name = platformConfig.name();
strings.find('string[@name="app_name"]').text = name.replace(/\'/g, '\\\'');
strings.find('string[@name="app_name"]').text = name.replace(/'/g, '\\\'');
var shortName = platformConfig.shortName && platformConfig.shortName();
if (shortName && shortName !== name) {
strings.find('string[@name="launcher_name"]').text = shortName.replace(/\'/g, '\\\'');
strings.find('string[@name="launcher_name"]').text = shortName.replace(/'/g, '\\\'');
}
fs.writeFileSync(locations.strings, strings.write({ indent: 4 }), 'utf-8');
@@ -212,9 +236,10 @@ function updateProjectAccordingTo (platformConfig, locations) {
.write();
// Java file paths shouldn't be hard coded
var javaPattern = path.join(locations.javaSrc, manifestId.replace(/\./g, '/'), '*.java');
var java_files = shell.ls(javaPattern).filter(function (f) {
return shell.grep(/extends\s+CordovaActivity/g, f);
const javaDirectory = path.join(locations.javaSrc, manifestId.replace(/\./g, '/'));
const javaPattern = /\.java$/;
const java_files = utils.scanDirectory(javaDirectory, javaPattern, true).filter(function (f) {
return utils.grep(f, /extends\s+CordovaActivity/g) !== null;
});
if (java_files.length === 0) {
@@ -223,18 +248,24 @@ function updateProjectAccordingTo (platformConfig, locations) {
events.emit('log', 'Multiple candidate Java files that extend CordovaActivity found. Guessing at the first one, ' + java_files[0]);
}
var destFile = path.join(locations.root, 'app', 'src', 'main', 'java', androidPkgName.replace(/\./g, '/'), path.basename(java_files[0]));
shell.mkdir('-p', path.dirname(destFile));
shell.sed(/package [\w\.]*;/, 'package ' + androidPkgName + ';', java_files[0]).to(destFile);
const destFile = 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;
var removeOrigPkg = checkReqs.isWindows() || checkReqs.isDarwin()
? manifestId.toUpperCase() !== androidPkgName.toUpperCase()
: manifestId !== androidPkgName;
if (removeOrigPkg) {
// If package was name changed we need to remove old java with main activity
shell.rm('-Rf', java_files[0]);
fs.removeSync(java_files[0]);
// remove any empty directories
var currentDir = path.dirname(java_files[0]);
var sourcesRoot = path.resolve(locations.root, 'src');
@@ -334,7 +365,7 @@ function cleanSplashes (projectRoot, projectConfig, platformResourcesDir) {
}
function updateIcons (cordovaProject, platformResourcesDir) {
let icons = cordovaProject.projectConfig.getIcons('android');
const icons = cordovaProject.projectConfig.getIcons('android');
// Skip if there are no app defined icons in config.xml
if (icons.length === 0) {
@@ -344,14 +375,14 @@ function updateIcons (cordovaProject, platformResourcesDir) {
// 1. loop icons determin if there is an error in the setup.
// 2. during initial loop, also setup for legacy support.
let errorMissingAttributes = [];
let errorLegacyIconNeeded = [];
const errorMissingAttributes = [];
const errorLegacyIconNeeded = [];
let hasAdaptive = false;
icons.forEach((icon, key) => {
if (
(icon.background && !icon.foreground)
|| (!icon.background && icon.foreground)
|| (!icon.background && !icon.foreground && !icon.src)
(icon.background && !icon.foreground) ||
(!icon.background && icon.foreground) ||
(!icon.background && !icon.foreground && !icon.src)
) {
errorMissingAttributes.push(icon.density ? icon.density : 'size=' + (icon.height || icon.width));
}
@@ -360,10 +391,10 @@ function updateIcons (cordovaProject, platformResourcesDir) {
hasAdaptive = true;
if (
!icon.src
&& (
icon.foreground.startsWith('@color')
|| path.extname(path.basename(icon.foreground)) === '.xml'
!icon.src &&
(
icon.foreground.startsWith('@color') ||
path.extname(path.basename(icon.foreground)) === '.xml'
)
) {
errorLegacyIconNeeded.push(icon.density ? icon.density : 'size=' + (icon.height || icon.width));
@@ -373,7 +404,7 @@ function updateIcons (cordovaProject, platformResourcesDir) {
}
});
let errorMessage = [];
const errorMessage = [];
if (errorMissingAttributes.length > 0) {
errorMessage.push('One of the following attributes are set but missing the other for the density type: ' + errorMissingAttributes.join(', ') + '. Please ensure that all require attributes are defined.');
}
@@ -396,7 +427,7 @@ function updateIcons (cordovaProject, platformResourcesDir) {
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher.xml')
);
let preparedIcons = prepareIcons(icons);
const preparedIcons = prepareIcons(icons);
if (hasAdaptive) {
resourceMap = updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
@@ -409,8 +440,8 @@ function updateIcons (cordovaProject, platformResourcesDir) {
}
function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformResourcesDir) {
let android_icons = preparedIcons.android_icons;
let default_icon = preparedIcons.default_icon;
const android_icons = preparedIcons.android_icons;
const default_icon = preparedIcons.default_icon;
// The source paths for icons and splashes are relative to
// project's config.xml location, so we use it as base path.
@@ -419,13 +450,18 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
let targetPathBackground;
let targetPathForeground;
for (let density in android_icons) {
for (const density in android_icons) {
let backgroundVal = '@mipmap/ic_launcher_background';
let foregroundVal = '@mipmap/ic_launcher_foreground';
background = android_icons[density].background;
foreground = android_icons[density].foreground;
if (!background || !foreground) {
// This icon isn't an adaptive icon, so skip it
continue;
}
if (background.startsWith('@color')) {
// Colors Use Case
backgroundVal = background; // Example: @color/background_foobar_1
@@ -459,7 +495,7 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
<foreground android:drawable="` + foregroundVal + `" />
</adaptive-icon>`;
let launcherXmlPath = path.join(platformResourcesDir, 'mipmap-' + density + '-v26', 'ic_launcher.xml');
const launcherXmlPath = path.join(platformResourcesDir, 'mipmap-' + density + '-v26', 'ic_launcher.xml');
// Remove the XML from the resourceMap so the file does not get removed.
delete resourceMap[launcherXmlPath];
@@ -503,8 +539,8 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
}
function updateIconResourceForLegacy (preparedIcons, resourceMap, platformResourcesDir) {
let android_icons = preparedIcons.android_icons;
let default_icon = preparedIcons.default_icon;
const android_icons = preparedIcons.android_icons;
const default_icon = preparedIcons.default_icon;
// The source paths for icons and splashes are relative to
// project's config.xml location, so we use it as base path.
@@ -533,7 +569,7 @@ function prepareIcons (icons) {
192: 'xxxhdpi'
};
let android_icons = {};
const android_icons = {};
let default_icon;
// find the best matching icon for a given density or size
@@ -563,8 +599,8 @@ function prepareIcons (icons) {
if (!size && !icon.density) {
if (default_icon) {
let found = {};
let favor = {};
const found = {};
const favor = {};
// populating found icon.
if (icon.background && icon.foreground) {
@@ -607,7 +643,7 @@ function cleanIcons (projectRoot, projectConfig, platformResourcesDir) {
return;
}
let resourceMap = Object.assign(
const resourceMap = Object.assign(
{},
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher.png'),
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_foreground.png'),
@@ -627,9 +663,10 @@ function cleanIcons (projectRoot, projectConfig, platformResourcesDir) {
* Gets a map containing resources of a specified name from all drawable folders in a directory.
*/
function mapImageResources (rootDir, subDir, type, resourceName) {
var pathMap = {};
shell.ls(path.join(rootDir, subDir, type + '-*')).forEach(function (drawableFolder) {
var imagePath = path.join(subDir, path.basename(drawableFolder), resourceName);
const pathMap = {};
const pattern = new RegExp(type + '+-.+');
utils.scanDirectory(path.join(rootDir, subDir), pattern).forEach(function (drawableFolder) {
const imagePath = path.join(subDir, path.basename(drawableFolder), resourceName);
pathMap[imagePath] = null;
});
return pathMap;
@@ -667,8 +704,7 @@ function cleanFileResources (projectRoot, projectConfig, platformDir) {
});
FileUpdater.updatePaths(
resourceMap, {
rootDir: projectRoot, all: true }, logFileOp);
resourceMap, { rootDir: projectRoot, all: true }, logFileOp);
}
}

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -34,7 +32,6 @@ var events = require('cordova-common').events;
* @returns {Promise}
*/
module.exports.retryPromise = function (attemptsLeft, promiseFunction) {
// NOTE:
// get all trailing arguments, by skipping the first two (attemptsLeft and
// promiseFunction) because they shouldn't get passed to promiseFunction

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -22,9 +20,8 @@
var path = require('path');
var emulator = require('./emulator');
var device = require('./device');
var Q = require('q');
var PackageType = require('./PackageType');
var events = require('cordova-common').events;
const { CordovaError, events } = require('cordova-common');
function getInstallTarget (runOptions) {
var install_target;
@@ -55,7 +52,7 @@ module.exports.run = function (runOptions) {
var self = this;
var install_target = getInstallTarget(runOptions);
return Q().then(function () {
return Promise.resolve().then(function () {
if (!install_target) {
// no target given, deploy to device if available, otherwise use the emulator.
return device.list().then(function (device_list) {
@@ -97,7 +94,7 @@ module.exports.run = function (runOptions) {
});
}
}
return Q.reject('Target \'' + install_target + '\' not found, unable to run project');
return Promise.reject(new CordovaError(`Target '${install_target}' not found, unable to run project`));
});
});
});

View File

@@ -5,9 +5,9 @@
:: 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

97
bin/templates/cordova/lib/utils.js vendored Normal file
View File

@@ -0,0 +1,97 @@
/*
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.
*/
/*
Provides a set of utility methods, which can also be spied on during unit tests.
*/
// TODO: Perhaps this should live in cordova-common?
const fs = require('fs-extra');
const path = require('path');
/**
* Reads, searches, and replaces the found occurences with replacementString and then writes the file back out.
* A backup is not made.
*
* @param {string} file A file path to a readable & writable file
* @param {RegExp} searchRegex The search regex
* @param {string} replacementString The string to replace the found occurences
* @returns void
*/
exports.replaceFileContents = function (file, searchRegex, replacementString) {
let contents = fs.readFileSync(file).toString();
contents = contents.replace(searchRegex, replacementString);
fs.writeFileSync(file, contents);
};
/**
* Reads a file and scans for regex. Returns the line of the first occurence or null if no occurences are found.
*
* @param {string} file A file path
* @param {RegExp} regex A search regex
* @returns string|null
*/
exports.grep = function (file, regex) {
const contents = fs.readFileSync(file).toString().replace(/\\r/g, '').split('\n');
for (let i = 0; i < contents.length; i++) {
const line = contents[i];
if (regex.test(line)) {
return line;
}
}
return null;
};
/**
* Scans directories and outputs a list of found paths that matches the regex
*
* @param {string} directory The starting directory
* @param {RegExp} regex The search regex
* @param {boolean} recursive Enables recursion
* @returns Array<string>
*/
exports.scanDirectory = function (directory, regex, recursive) {
let output = [];
if (fs.existsSync(directory)) {
const items = fs.readdirSync(directory);
for (let i = 0; i < items.length; i++) {
const item = items[i];
const itemPath = path.join(directory, item);
const stats = fs.statSync(itemPath);
if (regex.test(itemPath)) {
output.push(itemPath);
}
if (stats.isDirectory()) {
if (recursive) {
output = output.concat(exports.scanDirectory(itemPath, regex, recursive));
} else {
// Move onto the next item
continue;
}
}
}
}
return output;
};

View File

@@ -27,7 +27,7 @@ var args = process.argv;
if (args.length > 2) {
log.help();
} else {
reqs.run().done(function () {
reqs.run().then(function () {
return log.run();
}, function (err) {
console.error('ERROR: ' + err);

View File

@@ -30,17 +30,17 @@ if (['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >=
// Do some basic argument parsing
var runOpts = nopt({
'verbose': Boolean,
'silent': Boolean,
'debug': Boolean,
'release': Boolean,
'nobuild': Boolean,
'buildConfig': path,
'archs': String,
'device': Boolean,
'emulator': Boolean,
'target': String
}, { 'd': '--verbose' });
verbose: Boolean,
silent: Boolean,
debug: Boolean,
release: Boolean,
nobuild: Boolean,
buildConfig: path,
archs: String,
device: Boolean,
emulator: Boolean,
target: String
}, { d: '--verbose' });
// Make runOptions compatible with PlatformApi run method spec
runOpts.argv = runOpts.argv.remain;

View File

@@ -19,11 +19,6 @@
under the License.
*/
// Coho updates this line:
var VERSION = "8.1.0-dev";
const Api = require('./Api');
module.exports.version = VERSION;
if (!module.parent) {
console.log(VERSION);
}
console.log(Api.version());

View File

@@ -17,7 +17,7 @@
specific language governing permissions and limitations
under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="__PACKAGE__" android:versionName="1.0" android:versionCode="1" android:hardwareAccelerated="true">
<supports-screens
android:largeScreens="true"

View File

@@ -19,7 +19,31 @@
apply plugin: 'com.android.application'
if (cdvHelpers.getConfigPreference('GradlePluginKotlinEnabled', 'false').toBoolean()) {
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
}
buildscript {
apply from: '../CordovaLib/cordova.gradle'
if(cdvHelpers.getConfigPreference('GradlePluginKotlinEnabled', 'false').toBoolean()) {
String defaultGradlePluginKotlinVersion = kotlin_version
/**
* Fetches the user's defined Kotlin Version from config.xml.
* If the version is not set or invalid, it will default to the ${defaultGradlePluginKotlinVersion}
*/
String gradlePluginKotlinVersion = cdvHelpers.getConfigPreference('GradlePluginKotlinVersion', defaultGradlePluginKotlinVersion)
if(!cdvHelpers.isVersionValid(gradlePluginKotlinVersion)) {
println("The defined Kotlin version (${gradlePluginKotlinVersion}) does not appear to be a valid version. Falling back to version: ${defaultGradlePluginKotlinVersion}.")
gradlePluginKotlinVersion = defaultGradlePluginKotlinVersion
}
// Change the version to be used.
ext.kotlin_version = gradlePluginKotlinVersion
}
repositories {
mavenCentral()
google()
@@ -27,7 +51,32 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.0'
apply from: '../CordovaLib/cordova.gradle'
classpath 'com.android.tools.build:gradle:4.0.0'
if (cdvHelpers.getConfigPreference('GradlePluginKotlinEnabled', 'false').toBoolean()) {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
if(cdvHelpers.getConfigPreference('GradlePluginGoogleServicesEnabled', 'false').toBoolean()) {
String defaultGradlePluginGoogleServicesVersion = '4.2.0'
/**
* Fetches the user's defined Google Services Plugin Version from config.xml.
* If the version is not set or invalid, it will default to the ${defaultGradlePluginGoogleServicesVersion}
*/
String gradlePluginGoogleServicesVersion = cdvHelpers.getConfigPreference('GradlePluginGoogleServicesVersion', defaultGradlePluginGoogleServicesVersion)
if(!cdvHelpers.isVersionValid(gradlePluginGoogleServicesVersion)) {
println("The defined Google Services plugin version (${gradlePluginGoogleServicesVersion}) does not appear to be a valid version. Falling back to version: ${defaultGradlePluginGoogleServicesVersion}.")
gradlePluginGoogleServicesVersion = defaultGradlePluginGoogleServicesVersion
}
// Create the Google Services classpath and set it.
String gradlePluginGoogleServicesClassPath = "com.google.gms:google-services:${gradlePluginGoogleServicesVersion}"
println "Adding classpath: ${gradlePluginGoogleServicesClassPath}"
classpath gradlePluginGoogleServicesClassPath
}
}
}
@@ -40,7 +89,7 @@ allprojects {
}
task wrapper(type: Wrapper) {
gradleVersion = '4.10.3'
gradleVersion = '6.5'
}
// Configuration properties. Set these via environment variables, build-extras.gradle, or gradle.properties.
@@ -112,9 +161,9 @@ if (hasBuildExtras2) {
// Set property defaults after extension .gradle files.
ext.cdvCompileSdkVersion = cdvCompileSdkVersion == null ? (
defaultCompileSdkVersion == null
defaultCompileSdkVersion == null
? privateHelpers.getProjectTarget()
: defaultCompileSdkVersion
: defaultCompileSdkVersion
) : Integer.parseInt('' + cdvCompileSdkVersion);
if (ext.cdvBuildToolsVersion == null) {
@@ -280,6 +329,10 @@ android {
if (cdvDebugSigningPropertiesFile) {
addSigningProps(cdvDebugSigningPropertiesFile, signingConfigs.debug)
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
}
/*
@@ -291,6 +344,11 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: '*.jar')
if (cdvHelpers.getConfigPreference('GradlePluginKotlinEnabled', 'false').toBoolean()) {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
// SUB-PROJECT DEPENDENCIES START
debugCompile(project(path: ":CordovaLib", configuration: "debug"))
releaseCompile(project(path: ":CordovaLib", configuration: "release"))
@@ -356,3 +414,7 @@ for (def func : cdvPluginPostBuildExtras) {
if (hasProperty('postBuildExtras')) {
postBuildExtras()
}
if (cdvHelpers.getConfigPreference('GradlePluginGoogleServicesEnabled', 'false').toBoolean()) {
apply plugin: 'com.google.gms.google-services'
}

View File

@@ -1,5 +1,5 @@
// Platform: android
// 882658ab17740dbdece764e68c1f1f1f44fe3f9d
// 538a985db128858c0a0eb4dd40fb9c8e5433fc94
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -8,9 +8,9 @@
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
@@ -19,9 +19,8 @@
under the License.
*/
;(function() {
var PLATFORM_VERSION_BUILD_LABEL = '8.1.0-dev';
var PLATFORM_VERSION_BUILD_LABEL = '9.0.0';
// file: src/scripts/require.js
var require;
var define;
@@ -51,10 +50,10 @@ var define;
require = function (id) {
if (!modules[id]) {
throw 'module ' + id + ' not found';
throw new Error('module ' + id + ' not found');
} else if (id in inProgressModules) {
var cycle = requireStack.slice(inProgressModules[id]).join('->') + '->' + id;
throw 'Cycle in require graph: ' + cycle;
throw new Error('Cycle in require graph: ' + cycle);
}
if (modules[id].factory) {
try {
@@ -70,8 +69,8 @@ var define;
};
define = function (id, factory) {
if (modules[id]) {
throw 'module ' + id + ' already defined';
if (Object.prototype.hasOwnProperty.call(modules, id)) {
throw new Error('module ' + id + ' already defined');
}
modules[id] = {
@@ -98,7 +97,7 @@ define("cordova", function(require, exports, module) {
// Workaround for Windows 10 in hosted environment case
// http://www.w3.org/html/wg/drafts/html/master/browsers.html#named-access-on-the-window-object
if (window.cordova && !(window.cordova instanceof HTMLElement)) { // eslint-disable-line no-undef
if (window.cordova && !(window.cordova instanceof HTMLElement)) {
throw new Error('cordova already defined');
}
@@ -163,7 +162,7 @@ function createEvent (type, data) {
event.initEvent(type, false, false);
if (data) {
for (var i in data) {
if (data.hasOwnProperty(i)) {
if (Object.prototype.hasOwnProperty.call(data, i)) {
event[i] = data[i];
}
}
@@ -171,7 +170,6 @@ function createEvent (type, data) {
return event;
}
/* eslint-disable no-undef */
var cordova = {
define: define,
require: require,
@@ -179,8 +177,6 @@ var cordova = {
platformVersion: PLATFORM_VERSION_BUILD_LABEL,
platformId: platform.id,
/* eslint-enable no-undef */
/**
* Methods to add/remove your own addEventListener hijacking on document + window.
*/
@@ -199,15 +195,25 @@ var cordova = {
removeDocumentEventHandler: function (event) {
delete documentEventHandlers[event];
},
/**
* Retrieve original event handlers that were replaced by Cordova
*
* @return object
*/
getOriginalHandlers: function () {
return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener},
'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}};
return {
document: {
addEventListener: m_document_addEventListener,
removeEventListener: m_document_removeEventListener
},
window: {
addEventListener: m_window_addEventListener,
removeEventListener: m_window_removeEventListener
}
};
},
/**
* Method to fire event from native code
* bNoDetach is required for events which cause an exception which needs to be caught in native code
@@ -230,6 +236,7 @@ var cordova = {
document.dispatchEvent(evt);
}
},
fireWindowEvent: function (type, data) {
var evt = createEvent(type, data);
if (typeof windowEventHandlers[type] !== 'undefined') {
@@ -303,12 +310,11 @@ var cordova = {
}
} catch (err) {
var msg = 'Error in ' + (isSuccess ? 'Success' : 'Error') + ' callbackId: ' + callbackId + ' : ' + err;
console && console.log && console.log(msg);
console && console.log && err.stack && console.log(err.stack);
cordova.fireWindowEvent('cordovacallbackerror', { 'message': msg });
cordova.fireWindowEvent('cordovacallbackerror', { message: msg, error: err });
throw err;
}
},
addConstructor: function (func) {
channel.onCordovaReady.subscribe(function () {
try {
@@ -324,7 +330,7 @@ module.exports = cordova;
});
// file: /Users/erisu/git/apache/cordova/cordova-android/cordova-js-src/android/nativeapiprovider.js
// file: ../cordova-android/cordova-js-src/android/nativeapiprovider.js
define("cordova/android/nativeapiprovider", function(require, exports, module) {
/**
@@ -335,19 +341,19 @@ var nativeApi = this._cordovaNative || require('cordova/android/promptbasednativ
var currentApi = nativeApi;
module.exports = {
get: function() { return currentApi; },
setPreferPrompt: function(value) {
get: function () { return currentApi; },
setPreferPrompt: function (value) {
currentApi = value ? require('cordova/android/promptbasednativeapi') : nativeApi;
},
// Used only by tests.
set: function(value) {
set: function (value) {
currentApi = value;
}
};
});
// file: /Users/erisu/git/apache/cordova/cordova-android/cordova-js-src/android/promptbasednativeapi.js
// file: ../cordova-android/cordova-js-src/android/promptbasednativeapi.js
define("cordova/android/promptbasednativeapi", function(require, exports, module) {
/**
@@ -356,13 +362,13 @@ define("cordova/android/promptbasednativeapi", function(require, exports, module
*/
module.exports = {
exec: function(bridgeSecret, service, action, callbackId, argsJson) {
return prompt(argsJson, 'gap:'+JSON.stringify([bridgeSecret, service, action, callbackId]));
exec: function (bridgeSecret, service, action, callbackId, argsJson) {
return prompt(argsJson, 'gap:' + JSON.stringify([bridgeSecret, service, action, callbackId]));
},
setNativeToJsBridgeMode: function(bridgeSecret, value) {
setNativeToJsBridgeMode: function (bridgeSecret, value) {
prompt(value, 'gap_bridge_mode:' + bridgeSecret);
},
retrieveJsMessages: function(bridgeSecret, fromOnlineEvent) {
retrieveJsMessages: function (bridgeSecret, fromOnlineEvent) {
return prompt(+fromOnlineEvent, 'gap_poll:' + bridgeSecret);
}
};
@@ -377,18 +383,47 @@ var utils = require('cordova/utils');
var moduleExports = module.exports;
var typeMap = {
'A': 'Array',
'D': 'Date',
'N': 'Number',
'S': 'String',
'F': 'Function',
'O': 'Object'
A: 'Array',
D: 'Date',
N: 'Number',
S: 'String',
F: 'Function',
O: 'Object'
};
function extractParamName (callee, argIndex) {
return (/.*?\((.*?)\)/).exec(callee)[1].split(', ')[argIndex];
return (/\(\s*([^)]*?)\s*\)/).exec(callee)[1].split(/\s*,\s*/)[argIndex];
}
/**
* Checks the given arguments' types and throws if they are not as expected.
*
* `spec` is a string where each character stands for the required type of the
* argument at the same position. In other words: the character at `spec[i]`
* specifies the required type for `args[i]`. The characters in `spec` are the
* first letter of the required type's name. The supported types are:
*
* Array, Date, Number, String, Function, Object
*
* Lowercase characters specify arguments that must not be `null` or `undefined`
* while uppercase characters allow those values to be passed.
*
* Finally, `*` can be used to allow any type at the corresponding position.
*
* @example
* function foo (arr, opts) {
* // require `arr` to be an Array and `opts` an Object, null or undefined
* checkArgs('aO', 'my.package.foo', arguments);
* // ...
* }
* @param {String} spec - the type specification for `args` as described above
* @param {String} functionName - full name of the callee.
* Used in the error message
* @param {Array|arguments} args - the arguments to be checked against `spec`
* @param {Function} [opt_callee=args.callee] - the recipient of `args`.
* Used to extract parameter names for the error message
* @throws {TypeError} if args do not satisfy spec
*/
function checkArgs (spec, functionName, args, opt_callee) {
if (!moduleExports.enableChecks) {
return;
@@ -444,7 +479,7 @@ base64.fromArrayBuffer = function (arrayBuffer) {
};
base64.toArrayBuffer = function (str) {
var decodedStr = typeof atob !== 'undefined' ? atob(str) : Buffer.from(str, 'base64').toString('binary'); // eslint-disable-line no-undef
var decodedStr = atob(str);
var arrayBuffer = new ArrayBuffer(decodedStr.length);
var array = new Uint8Array(arrayBuffer);
for (var i = 0, len = decodedStr.length; i < len; i++) {
@@ -506,14 +541,13 @@ var utils = require('cordova/utils');
function each (objects, func, context) {
for (var prop in objects) {
if (objects.hasOwnProperty(prop)) {
if (Object.prototype.hasOwnProperty.call(objects, prop)) {
func.apply(context, [objects[prop], prop]);
}
}
}
function clobber (obj, key, value) {
exports.replaceHookForTesting(obj, key);
var needsProperty = false;
try {
obj[key] = value;
@@ -587,7 +621,7 @@ function include (parent, objects, clobber, merge) {
*/
function recursiveMerge (target, src) {
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
if (Object.prototype.hasOwnProperty.call(src, prop)) {
if (target.prototype && target.prototype.constructor === target) {
// If the target object is a constructor override off prototype.
clobber(target.prototype, prop, src[prop]);
@@ -613,7 +647,6 @@ exports.buildIntoAndMerge = function (objects, target) {
};
exports.recursiveMerge = recursiveMerge;
exports.assignOrWrapInDeprecateGetter = assignOrWrapInDeprecateGetter;
exports.replaceHookForTesting = function () {};
});
@@ -694,14 +727,14 @@ var channel = {
}
if (!len) h();
},
/* eslint-disable no-return-assign */
create: function (type) {
return channel[type] = new Channel(type, false);
return (channel[type] = new Channel(type, false));
},
createSticky: function (type) {
return channel[type] = new Channel(type, true);
return (channel[type] = new Channel(type, true));
},
/* eslint-enable no-return-assign */
/**
* cordova Channels that must fire before "deviceready" is fired.
*/
@@ -822,7 +855,6 @@ Channel.prototype.unsubscribe = function (eventListenerOrFunction) {
* Calls all functions subscribed to this channel.
*/
Channel.prototype.fire = function (e) {
var fail = false; // eslint-disable-line no-unused-vars
var fireArgs = Array.prototype.slice.call(arguments);
// Apply stickiness.
if (this.state === 1) {
@@ -879,7 +911,7 @@ module.exports = channel;
});
// file: /Users/erisu/git/apache/cordova/cordova-android/cordova-js-src/exec.js
// file: ../cordova-android/cordova-js-src/exec.js
define("cordova/exec", function(require, exports, module) {
/**
@@ -896,38 +928,38 @@ define("cordova/exec", function(require, exports, module) {
* @param {String} action Action to be run in cordova
* @param {String[]} [args] Zero or more arguments to pass to the method
*/
var cordova = require('cordova'),
nativeApiProvider = require('cordova/android/nativeapiprovider'),
utils = require('cordova/utils'),
base64 = require('cordova/base64'),
channel = require('cordova/channel'),
jsToNativeModes = {
PROMPT: 0,
JS_OBJECT: 1
},
nativeToJsModes = {
// Polls for messages using the JS->Native bridge.
POLLING: 0,
// For LOAD_URL to be viable, it would need to have a work-around for
// the bug where the soft-keyboard gets dismissed when a message is sent.
LOAD_URL: 1,
// For the ONLINE_EVENT to be viable, it would need to intercept all event
// listeners (both through addEventListener and window.ononline) as well
// as set the navigator property itself.
ONLINE_EVENT: 2,
EVAL_BRIDGE: 3
},
jsToNativeBridgeMode, // Set lazily.
nativeToJsBridgeMode = nativeToJsModes.EVAL_BRIDGE,
pollEnabled = false,
bridgeSecret = -1;
var cordova = require('cordova');
var nativeApiProvider = require('cordova/android/nativeapiprovider');
var utils = require('cordova/utils');
var base64 = require('cordova/base64');
var channel = require('cordova/channel');
var jsToNativeModes = {
PROMPT: 0,
JS_OBJECT: 1
};
var nativeToJsModes = {
// Polls for messages using the JS->Native bridge.
POLLING: 0,
// For LOAD_URL to be viable, it would need to have a work-around for
// the bug where the soft-keyboard gets dismissed when a message is sent.
LOAD_URL: 1,
// For the ONLINE_EVENT to be viable, it would need to intercept all event
// listeners (both through addEventListener and window.ononline) as well
// as set the navigator property itself.
ONLINE_EVENT: 2,
EVAL_BRIDGE: 3
};
var jsToNativeBridgeMode; // Set lazily.
var nativeToJsBridgeMode = nativeToJsModes.EVAL_BRIDGE;
var pollEnabled = false;
var bridgeSecret = -1;
var messagesFromNative = [];
var isProcessing = false;
var resolvedPromise = typeof Promise == 'undefined' ? null : Promise.resolve();
var nextTick = resolvedPromise ? function(fn) { resolvedPromise.then(fn); } : function(fn) { setTimeout(fn); };
var resolvedPromise = typeof Promise === 'undefined' ? null : Promise.resolve();
var nextTick = resolvedPromise ? function (fn) { resolvedPromise.then(fn); } : function (fn) { setTimeout(fn); };
function androidExec(success, fail, service, action, args) {
function androidExec (success, fail, service, action, args) {
if (bridgeSecret < 0) {
// If we ever catch this firing, we'll need to queue up exec()s
// and fire them once we get a secret. For now, I don't think
@@ -946,21 +978,21 @@ function androidExec(success, fail, service, action, args) {
// Process any ArrayBuffers in the args into a string.
for (var i = 0; i < args.length; i++) {
if (utils.typeName(args[i]) == 'ArrayBuffer') {
if (utils.typeName(args[i]) === 'ArrayBuffer') {
args[i] = base64.fromArrayBuffer(args[i]);
}
}
var callbackId = service + cordova.callbackId++,
argsJson = JSON.stringify(args);
var callbackId = service + cordova.callbackId++;
var argsJson = JSON.stringify(args);
if (success || fail) {
cordova.callbacks[callbackId] = {success:success, fail:fail};
cordova.callbacks[callbackId] = { success: success, fail: fail };
}
var msgs = nativeApiProvider.get().exec(bridgeSecret, service, action, callbackId, argsJson);
// If argsJson was received by Java as null, try again with the PROMPT bridge mode.
// This happens in rare circumstances, such as when certain Unicode characters are passed over the bridge on a Galaxy S2. See CB-2666.
if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT && msgs === "@Null arguments.") {
if (jsToNativeBridgeMode === jsToNativeModes.JS_OBJECT && msgs === '@Null arguments.') {
androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT);
androidExec(success, fail, service, action, args);
androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
@@ -971,16 +1003,16 @@ function androidExec(success, fail, service, action, args) {
}
}
androidExec.init = function() {
androidExec.init = function () {
bridgeSecret = +prompt('', 'gap_init:' + nativeToJsBridgeMode);
channel.onNativeReady.fire();
};
function pollOnceFromOnlineEvent() {
function pollOnceFromOnlineEvent () {
pollOnce(true);
}
function pollOnce(opt_fromOnlineEvent) {
function pollOnce (opt_fromOnlineEvent) {
if (bridgeSecret < 0) {
// This can happen when the NativeToJsMessageQueue resets the online state on page transitions.
// We know there's nothing to retrieve, so no need to poll.
@@ -994,15 +1026,15 @@ function pollOnce(opt_fromOnlineEvent) {
}
}
function pollingTimerFunc() {
function pollingTimerFunc () {
if (pollEnabled) {
pollOnce();
setTimeout(pollingTimerFunc, 50);
}
}
function hookOnlineApis() {
function proxyEvent(e) {
function hookOnlineApis () {
function proxyEvent (e) {
cordova.fireWindowEvent(e.type);
}
// The network module takes care of firing online and offline events.
@@ -1022,19 +1054,19 @@ hookOnlineApis();
androidExec.jsToNativeModes = jsToNativeModes;
androidExec.nativeToJsModes = nativeToJsModes;
androidExec.setJsToNativeBridgeMode = function(mode) {
if (mode == jsToNativeModes.JS_OBJECT && !window._cordovaNative) {
androidExec.setJsToNativeBridgeMode = function (mode) {
if (mode === jsToNativeModes.JS_OBJECT && !window._cordovaNative) {
mode = jsToNativeModes.PROMPT;
}
nativeApiProvider.setPreferPrompt(mode == jsToNativeModes.PROMPT);
nativeApiProvider.setPreferPrompt(mode === jsToNativeModes.PROMPT);
jsToNativeBridgeMode = mode;
};
androidExec.setNativeToJsBridgeMode = function(mode) {
if (mode == nativeToJsBridgeMode) {
androidExec.setNativeToJsBridgeMode = function (mode) {
if (mode === nativeToJsBridgeMode) {
return;
}
if (nativeToJsBridgeMode == nativeToJsModes.POLLING) {
if (nativeToJsBridgeMode === nativeToJsModes.POLLING) {
pollEnabled = false;
}
@@ -1045,32 +1077,32 @@ androidExec.setNativeToJsBridgeMode = function(mode) {
nativeApiProvider.get().setNativeToJsBridgeMode(bridgeSecret, mode);
}
if (mode == nativeToJsModes.POLLING) {
if (mode === nativeToJsModes.POLLING) {
pollEnabled = true;
setTimeout(pollingTimerFunc, 1);
}
};
function buildPayload(payload, message) {
function buildPayload (payload, message) {
var payloadKind = message.charAt(0);
if (payloadKind == 's') {
if (payloadKind === 's') {
payload.push(message.slice(1));
} else if (payloadKind == 't') {
} else if (payloadKind === 't') {
payload.push(true);
} else if (payloadKind == 'f') {
} else if (payloadKind === 'f') {
payload.push(false);
} else if (payloadKind == 'N') {
} else if (payloadKind === 'N') {
payload.push(null);
} else if (payloadKind == 'n') {
} else if (payloadKind === 'n') {
payload.push(+message.slice(1));
} else if (payloadKind == 'A') {
} else if (payloadKind === 'A') {
var data = message.slice(1);
payload.push(base64.toArrayBuffer(data));
} else if (payloadKind == 'S') {
} else if (payloadKind === 'S') {
payload.push(window.atob(message.slice(1)));
} else if (payloadKind == 'M') {
} else if (payloadKind === 'M') {
var multipartMessages = message.slice(1);
while (multipartMessages !== "") {
while (multipartMessages !== '') {
var spaceIdx = multipartMessages.indexOf(' ');
var msgLen = +multipartMessages.slice(0, spaceIdx);
var multipartMessage = multipartMessages.substr(spaceIdx + 1, msgLen);
@@ -1083,14 +1115,15 @@ function buildPayload(payload, message) {
}
// Processes a single message, as encoded by NativeToJsMessageQueue.java.
function processMessage(message) {
function processMessage (message) {
var firstChar = message.charAt(0);
if (firstChar == 'J') {
if (firstChar === 'J') {
// This is deprecated on the .java side. It doesn't work with CSP enabled.
// eslint-disable-next-line no-eval
eval(message.slice(1));
} else if (firstChar == 'S' || firstChar == 'F') {
var success = firstChar == 'S';
var keepCallback = message.charAt(1) == '1';
} else if (firstChar === 'S' || firstChar === 'F') {
var success = firstChar === 'S';
var keepCallback = message.charAt(1) === '1';
var spaceIdx = message.indexOf(' ', 2);
var status = +message.slice(2, spaceIdx);
var nextSpaceIdx = message.indexOf(' ', spaceIdx + 1);
@@ -1100,11 +1133,11 @@ function processMessage(message) {
buildPayload(payload, payloadMessage);
cordova.callbackFromNative(callbackId, success, status, payload, keepCallback);
} else {
console.log("processMessage failed: invalid message: " + JSON.stringify(message));
console.log('processMessage failed: invalid message: ' + JSON.stringify(message));
}
}
function processMessages() {
function processMessages () {
// Check for the reentrant case.
if (isProcessing) {
return;
@@ -1117,7 +1150,7 @@ function processMessages() {
var msg = popMessageFromQueue();
// The Java side can send a * message to indicate that it
// still has messages waiting to be retrieved.
if (msg == '*' && messagesFromNative.length === 0) {
if (msg === '*' && messagesFromNative.length === 0) {
nextTick(pollOnce);
return;
}
@@ -1130,9 +1163,9 @@ function processMessages() {
}
}
function popMessageFromQueue() {
function popMessageFromQueue () {
var messageBatch = messagesFromNative.shift();
if (messageBatch == '*') {
if (messageBatch === '*') {
return '*';
}
@@ -1188,7 +1221,6 @@ var cordova = require('cordova');
var modulemapper = require('cordova/modulemapper');
var platform = require('cordova/platform');
var pluginloader = require('cordova/pluginloader');
var utils = require('cordova/utils');
var platformInitChannelsArray = [channel.onNativeReady, channel.onPluginsReady];
@@ -1208,34 +1240,6 @@ window.setTimeout(function () {
}
}, 5000);
// Replace navigator before any modules are required(), to ensure it happens as soon as possible.
// We replace it so that properties that can't be clobbered can instead be overridden.
function replaceNavigator (origNavigator) {
var CordovaNavigator = function () {};
CordovaNavigator.prototype = origNavigator;
var newNavigator = new CordovaNavigator();
// This work-around really only applies to new APIs that are newer than Function.bind.
// Without it, APIs such as getGamepads() break.
if (CordovaNavigator.bind) {
for (var key in origNavigator) {
if (typeof origNavigator[key] === 'function') {
newNavigator[key] = origNavigator[key].bind(origNavigator);
} else {
(function (k) {
utils.defineGetterSetter(newNavigator, key, function () {
return origNavigator[k];
});
})(key);
}
}
}
return newNavigator;
}
if (window.navigator) {
window.navigator = replaceNavigator(window.navigator);
}
if (!window.console) {
window.console = {
log: function () {}
@@ -1301,7 +1305,6 @@ channel.join(function () {
channel.join(function () {
require('cordova').fireDocumentEvent('deviceready');
}, channel.deviceReadyChannelsArray);
}, platformInitChannelsArray);
});
@@ -1310,7 +1313,7 @@ channel.join(function () {
define("cordova/modulemapper", function(require, exports, module) {
var builder = require('cordova/builder');
var moduleMap = define.moduleMap; // eslint-disable-line no-undef
var moduleMap = define.moduleMap;
var symbolList;
var deprecationMap;
@@ -1350,12 +1353,9 @@ function prepareNamespace (symbolPath, context) {
if (!symbolPath) {
return context;
}
var parts = symbolPath.split('.');
var cur = context;
for (var i = 0, part; part = parts[i]; ++i) { // eslint-disable-line no-cond-assign
cur = cur[part] = cur[part] || {};
}
return cur;
return symbolPath.split('.').reduce(function (cur, part) {
return (cur[part] = cur[part] || {});
}, context);
}
exports.mapModules = function (context) {
@@ -1406,7 +1406,7 @@ exports.reset();
});
// file: /Users/erisu/git/apache/cordova/cordova-android/cordova-js-src/platform.js
// file: ../cordova-android/cordova-js-src/platform.js
define("cordova/platform", function(require, exports, module) {
// The last resume event that was received that had the result of a plugin call.
@@ -1414,11 +1414,11 @@ var lastResumeEvent = null;
module.exports = {
id: 'android',
bootstrap: function() {
var channel = require('cordova/channel'),
cordova = require('cordova'),
exec = require('cordova/exec'),
modulemapper = require('cordova/modulemapper');
bootstrap: function () {
var channel = require('cordova/channel');
var cordova = require('cordova');
var exec = require('cordova/exec');
var modulemapper = require('cordova/modulemapper');
// Get the shared secret needed to use the bridge.
exec.init();
@@ -1430,21 +1430,21 @@ module.exports = {
// Inject a listener for the backbutton on the document.
var backButtonChannel = cordova.addDocumentEventHandler('backbutton');
backButtonChannel.onHasSubscribersChange = function() {
backButtonChannel.onHasSubscribersChange = function () {
// If we just attached the first handler or detached the last handler,
// let native know we need to override the back button.
exec(null, null, APP_PLUGIN_NAME, "overrideBackbutton", [this.numHandlers == 1]);
exec(null, null, APP_PLUGIN_NAME, 'overrideBackbutton', [this.numHandlers === 1]);
};
// Add hardware MENU and SEARCH button handlers
cordova.addDocumentEventHandler('menubutton');
cordova.addDocumentEventHandler('searchbutton');
function bindButtonChannel(buttonName) {
function bindButtonChannel (buttonName) {
// generic button bind used for volumeup/volumedown buttons
var volumeButtonChannel = cordova.addDocumentEventHandler(buttonName + 'button');
volumeButtonChannel.onHasSubscribersChange = function() {
exec(null, null, APP_PLUGIN_NAME, "overrideButton", [buttonName, this.numHandlers == 1]);
volumeButtonChannel.onHasSubscribersChange = function () {
exec(null, null, APP_PLUGIN_NAME, 'overrideButton', [buttonName, this.numHandlers === 1]);
};
}
// Inject a listener for the volume buttons on the document.
@@ -1456,7 +1456,7 @@ module.exports = {
// plugin result is delivered even after the event is fired (CB-10498)
var cordovaAddEventListener = document.addEventListener;
document.addEventListener = function(evt, handler, capture) {
document.addEventListener = function (evt, handler, capture) {
cordovaAddEventListener(evt, handler, capture);
if (evt === 'resume' && lastResumeEvent) {
@@ -1466,57 +1466,54 @@ module.exports = {
// Let native code know we are all done on the JS side.
// Native code will then un-hide the WebView.
channel.onCordovaReady.subscribe(function() {
channel.onCordovaReady.subscribe(function () {
exec(onMessageFromNative, null, APP_PLUGIN_NAME, 'messageChannel', []);
exec(null, null, APP_PLUGIN_NAME, "show", []);
exec(null, null, APP_PLUGIN_NAME, 'show', []);
});
}
};
function onMessageFromNative(msg) {
function onMessageFromNative (msg) {
var cordova = require('cordova');
var action = msg.action;
switch (action)
{
// Button events
case 'backbutton':
case 'menubutton':
case 'searchbutton':
// App life cycle events
case 'pause':
// Volume events
case 'volumedownbutton':
case 'volumeupbutton':
cordova.fireDocumentEvent(action);
break;
case 'resume':
if(arguments.length > 1 && msg.pendingResult) {
if(arguments.length === 2) {
msg.pendingResult.result = arguments[1];
} else {
// The plugin returned a multipart message
var res = [];
for(var i = 1; i < arguments.length; i++) {
res.push(arguments[i]);
}
msg.pendingResult.result = res;
switch (action) {
// pause and resume are Android app life cycle events
case 'backbutton':
case 'menubutton':
case 'searchbutton':
case 'pause':
case 'volumedownbutton':
case 'volumeupbutton':
cordova.fireDocumentEvent(action);
break;
case 'resume':
if (arguments.length > 1 && msg.pendingResult) {
if (arguments.length === 2) {
msg.pendingResult.result = arguments[1];
} else {
// The plugin returned a multipart message
var res = [];
for (var i = 1; i < arguments.length; i++) {
res.push(arguments[i]);
}
// Save the plugin result so that it can be delivered to the js
// even if they miss the initial firing of the event
lastResumeEvent = msg;
msg.pendingResult.result = res;
}
cordova.fireDocumentEvent(action, msg);
break;
default:
throw new Error('Unknown event action ' + action);
// Save the plugin result so that it can be delivered to the js
// even if they miss the initial firing of the event
lastResumeEvent = msg;
}
cordova.fireDocumentEvent(action, msg);
break;
default:
throw new Error('Unknown event action ' + action);
}
}
});
// file: /Users/erisu/git/apache/cordova/cordova-android/cordova-js-src/plugin/android/app.js
// file: ../cordova-android/cordova-js-src/plugin/android/app.js
define("cordova/plugin/android/app", function(require, exports, module) {
var exec = require('cordova/exec');
@@ -1526,8 +1523,8 @@ module.exports = {
/**
* Clear the resource cache.
*/
clearCache:function() {
exec(null, null, APP_PLUGIN_NAME, "clearCache", []);
clearCache: function () {
exec(null, null, APP_PLUGIN_NAME, 'clearCache', []);
},
/**
@@ -1544,31 +1541,31 @@ module.exports = {
* Example:
* navigator.app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000});
*/
loadUrl:function(url, props) {
exec(null, null, APP_PLUGIN_NAME, "loadUrl", [url, props]);
loadUrl: function (url, props) {
exec(null, null, APP_PLUGIN_NAME, 'loadUrl', [url, props]);
},
/**
* Cancel loadUrl that is waiting to be loaded.
*/
cancelLoadUrl:function() {
exec(null, null, APP_PLUGIN_NAME, "cancelLoadUrl", []);
cancelLoadUrl: function () {
exec(null, null, APP_PLUGIN_NAME, 'cancelLoadUrl', []);
},
/**
* Clear web history in this web view.
* Instead of BACK button loading the previous web page, it will exit the app.
*/
clearHistory:function() {
exec(null, null, APP_PLUGIN_NAME, "clearHistory", []);
clearHistory: function () {
exec(null, null, APP_PLUGIN_NAME, 'clearHistory', []);
},
/**
* Go to previous page displayed.
* This is the same as pressing the backbutton on Android device.
*/
backHistory:function() {
exec(null, null, APP_PLUGIN_NAME, "backHistory", []);
backHistory: function () {
exec(null, null, APP_PLUGIN_NAME, 'backHistory', []);
},
/**
@@ -1580,8 +1577,8 @@ module.exports = {
*
* @param override T=override, F=cancel override
*/
overrideBackbutton:function(override) {
exec(null, null, APP_PLUGIN_NAME, "overrideBackbutton", [override]);
overrideBackbutton: function (override) {
exec(null, null, APP_PLUGIN_NAME, 'overrideBackbutton', [override]);
},
/**
@@ -1595,15 +1592,15 @@ module.exports = {
* @param button volumeup, volumedown
* @param override T=override, F=cancel override
*/
overrideButton:function(button, override) {
exec(null, null, APP_PLUGIN_NAME, "overrideButton", [button, override]);
overrideButton: function (button, override) {
exec(null, null, APP_PLUGIN_NAME, 'overrideButton', [button, override]);
},
/**
* Exit and terminate the application.
*/
exitApp:function() {
return exec(null, null, APP_PLUGIN_NAME, "exitApp", []);
exitApp: function () {
return exec(null, null, APP_PLUGIN_NAME, 'exitApp', []);
}
};
@@ -1628,11 +1625,11 @@ exports.injectScript = function (url, onload, onerror) {
function injectIfNecessary (id, url, onload, onerror) {
onerror = onerror || onload;
if (id in define.moduleMap) { // eslint-disable-line no-undef
if (id in define.moduleMap) {
onload();
} else {
exports.injectScript(url, function () {
if (id in define.moduleMap) { // eslint-disable-line no-undef
if (id in define.moduleMap) {
onload();
} else {
onerror();
@@ -1643,7 +1640,7 @@ function injectIfNecessary (id, url, onload, onerror) {
function onScriptLoadingComplete (moduleList, finishPluginLoading) {
// Loop through all the plugins and then through their clobbers and merges.
for (var i = 0, module; module = moduleList[i]; i++) { // eslint-disable-line no-cond-assign
for (var i = 0, module; (module = moduleList[i]); i++) {
if (module.clobbers && module.clobbers.length) {
for (var j = 0; j < module.clobbers.length; j++) {
modulemapper.clobbers(module.id, module.clobbers[j]);
@@ -1826,10 +1823,11 @@ utils.clone = function (obj) {
retVal = {};
for (i in obj) {
// https://issues.apache.org/jira/browse/CB-11522 'unknown' type may be returned in
// custom protocol activation case on Windows Phone 8.1 causing "No such interface supported" exception
// on cloning.
if ((!(i in retVal) || retVal[i] !== obj[i]) && typeof obj[i] !== 'undefined' && typeof obj[i] !== 'unknown') { // eslint-disable-line valid-typeof
// 'unknown' type may be returned in custom protocol activation case on
// Windows Phone 8.1 causing "No such interface supported" exception on
// cloning (https://issues.apache.org/jira/browse/CB-11522)
// eslint-disable-next-line valid-typeof
if ((!(i in retVal) || retVal[i] !== obj[i]) && typeof obj[i] !== 'undefined' && typeof obj[i] !== 'unknown') {
retVal[i] = utils.clone(obj[i]);
}
}
@@ -1879,7 +1877,6 @@ utils.extend = (function () {
var F = function () {};
// extend Child from Parent
return function (Child, Parent) {
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.__super__ = Parent.prototype;
@@ -1902,7 +1899,6 @@ utils.alert = function (msg) {
window.cordova = require('cordova');
// file: src/scripts/bootstrap.js
require('cordova/init');
})();
})();

View File

@@ -102,13 +102,13 @@ h1 {
50% { opacity: 0.4; }
to { opacity: 1.0; }
}
@-webkit-keyframes fade {
from { opacity: 1.0; }
50% { opacity: 0.4; }
to { opacity: 1.0; }
}
.blink {
animation:fade 3000ms infinite;
-webkit-animation:fade 3000ms infinite;

View File

@@ -19,16 +19,16 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath 'com.android.tools.build:gradle:3.3.0'
}
}
@@ -40,10 +40,10 @@ allprojects {
//This replaces project.properties w.r.t. build settings
project.ext {
defaultBuildToolsVersion="28.0.3" //String
defaultMinSdkVersion=19 //Integer - Minimum requirement is Android 4.4
defaultTargetSdkVersion=28 //Integer - We ALWAYS target the latest by default
defaultCompileSdkVersion=28 //Integer - We ALWAYS compile with the latest by default
defaultBuildToolsVersion="29.0.2" //String
defaultMinSdkVersion=22 //Integer - Minimum requirement is Android 5.1
defaultTargetSdkVersion=29 //Integer - We ALWAYS target the latest by default
defaultCompileSdkVersion=29 //Integer - We ALWAYS compile with the latest by default
}
}

View File

@@ -1,334 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
apply plugin: 'com.android.application'
buildscript {
repositories {
mavenCentral()
jcenter()
}
// Switch the Android Gradle plugin version requirement depending on the
// installed version of Gradle. This dependency is documented at
// http://tools.android.com/tech-docs/new-build-system/version-compatibility
// and https://issues.apache.org/jira/browse/CB-8143
dependencies {
classpath 'com.android.tools.build:gradle:2.2.3'
}
}
// Allow plugins to declare Maven dependencies via build-extras.gradle.
allprojects {
repositories {
mavenCentral();
jcenter()
}
}
task wrapper(type: Wrapper) {
gradleVersion = '2.14.1'
}
// Configuration properties. Set these via environment variables, build-extras.gradle, or gradle.properties.
// Refer to: http://www.gradle.org/docs/current/userguide/tutorial_this_and_that.html
ext {
apply from: 'CordovaLib/cordova.gradle'
// The value for android.compileSdkVersion.
if (!project.hasProperty('cdvCompileSdkVersion')) {
cdvCompileSdkVersion = null;
}
// The value for android.buildToolsVersion.
if (!project.hasProperty('cdvBuildToolsVersion')) {
cdvBuildToolsVersion = null;
}
// Sets the versionCode to the given value.
if (!project.hasProperty('cdvVersionCode')) {
cdvVersionCode = null
}
// Sets the minSdkVersion to the given value.
if (!project.hasProperty('cdvMinSdkVersion')) {
cdvMinSdkVersion = null
}
// Sets the maxSdkVersion to the given value.
if (!project.hasProperty('cdvMaxSdkVersion')) {
cdvMaxSdkVersion = null
}
// The value for android.targetSdkVersion.
if (!project.hasProperty('cdvTargetSdkVersion')) {
cdvTargetSdkVersion = null;
}
// Whether to build architecture-specific APKs.
if (!project.hasProperty('cdvBuildMultipleApks')) {
cdvBuildMultipleApks = null
}
// .properties files to use for release signing.
if (!project.hasProperty('cdvReleaseSigningPropertiesFile')) {
cdvReleaseSigningPropertiesFile = null
}
// .properties files to use for debug signing.
if (!project.hasProperty('cdvDebugSigningPropertiesFile')) {
cdvDebugSigningPropertiesFile = null
}
// Set by build.js script.
if (!project.hasProperty('cdvBuildArch')) {
cdvBuildArch = null
}
// Plugin gradle extensions can append to this to have code run at the end.
cdvPluginPostBuildExtras = []
}
// PLUGIN GRADLE EXTENSIONS START
// PLUGIN GRADLE EXTENSIONS END
def hasBuildExtras = file('build-extras.gradle').exists()
if (hasBuildExtras) {
apply from: 'build-extras.gradle'
}
// Set property defaults after extension .gradle files.
if (ext.cdvCompileSdkVersion == null) {
ext.cdvCompileSdkVersion = privateHelpers.getProjectTarget()
}
if (ext.cdvBuildToolsVersion == null) {
ext.cdvBuildToolsVersion = privateHelpers.findLatestInstalledBuildTools()
}
if (ext.cdvDebugSigningPropertiesFile == null && file('debug-signing.properties').exists()) {
ext.cdvDebugSigningPropertiesFile = 'debug-signing.properties'
}
if (ext.cdvReleaseSigningPropertiesFile == null && file('release-signing.properties').exists()) {
ext.cdvReleaseSigningPropertiesFile = 'release-signing.properties'
}
// Cast to appropriate types.
ext.cdvBuildMultipleApks = cdvBuildMultipleApks == null ? false : cdvBuildMultipleApks.toBoolean();
ext.cdvMinSdkVersion = cdvMinSdkVersion == null ? null : Integer.parseInt('' + cdvMinSdkVersion)
if(cdvMaxSdkVersion != null) {
ext.cdvMaxSdkVersion = Integer.parseInt('' + cdvMaxSdkVersion)
}
ext.cdvVersionCode = cdvVersionCode == null ? null : Integer.parseInt('' + cdvVersionCode)
def computeBuildTargetName(debugBuild) {
def ret = 'assemble'
if (cdvBuildMultipleApks && cdvBuildArch) {
def arch = cdvBuildArch == 'arm' ? 'armv7' : cdvBuildArch
ret += '' + arch.toUpperCase().charAt(0) + arch.substring(1);
}
return ret + (debugBuild ? 'Debug' : 'Release')
}
// Make cdvBuild a task that depends on the debug/arch-sepecific task.
task cdvBuildDebug
cdvBuildDebug.dependsOn {
return computeBuildTargetName(true)
}
task cdvBuildRelease
cdvBuildRelease.dependsOn {
return computeBuildTargetName(false)
}
task cdvPrintProps << {
println('cdvCompileSdkVersion=' + cdvCompileSdkVersion)
println('cdvBuildToolsVersion=' + cdvBuildToolsVersion)
println('cdvVersionCode=' + cdvVersionCode)
println('cdvMinSdkVersion=' + cdvMinSdkVersion)
println('cdvMaxSdkVersion=' + cdvMaxSdkVersion)
println('cdvTargetSdkVersion=' + cdvTargetSdkVersion)
println('cdvBuildMultipleApks=' + cdvBuildMultipleApks)
println('cdvReleaseSigningPropertiesFile=' + cdvReleaseSigningPropertiesFile)
println('cdvDebugSigningPropertiesFile=' + cdvDebugSigningPropertiesFile)
println('cdvBuildArch=' + cdvBuildArch)
println('computedVersionCode=' + android.defaultConfig.versionCode)
android.productFlavors.each { flavor ->
println('computed' + flavor.name.capitalize() + 'VersionCode=' + flavor.versionCode)
}
}
android {
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
}
defaultConfig {
versionCode cdvVersionCode ?: new BigInteger("" + privateHelpers.extractIntFromManifest("versionCode"))
applicationId privateHelpers.extractStringFromManifest("package")
if (cdvMinSdkVersion != null) {
minSdkVersion cdvMinSdkVersion
}
if (cdvMaxSdkVersion != null) {
maxSdkVersion cdvMaxSdkVersion
}
if(cdvTargetSdkVersion != null) {
targetSdkVersion cdvTargetSdkVersion
}
}
lintOptions {
abortOnError false;
}
compileSdkVersion cdvCompileSdkVersion
buildToolsVersion cdvBuildToolsVersion
if (Boolean.valueOf(cdvBuildMultipleApks)) {
productFlavors {
armv7 {
versionCode defaultConfig.versionCode*10 + 2
ndk {
abiFilters "armeabi-v7a", ""
}
}
x86 {
versionCode defaultConfig.versionCode*10 + 4
ndk {
abiFilters "x86", ""
}
}
all {
ndk {
abiFilters "all", ""
}
}
}
}
/*
ELSE NOTHING! DON'T MESS WITH THE VERSION CODE IF YOU DON'T HAVE TO!
else if (!cdvVersionCode) {
def minSdkVersion = cdvMinSdkVersion ?: privateHelpers.extractIntFromManifest("minSdkVersion")
// Vary versionCode by the two most common API levels:
// 14 is ICS, which is the lowest API level for many apps.
// 20 is Lollipop, which is the lowest API level for the updatable system webview.
if (minSdkVersion >= 20) {
defaultConfig.versionCode += 9
} else if (minSdkVersion >= 14) {
defaultConfig.versionCode += 8
}
}
*/
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
targetCompatibility JavaVersion.VERSION_1_6
}
if (cdvReleaseSigningPropertiesFile) {
signingConfigs {
release {
// These must be set or Gradle will complain (even if they are overridden).
keyAlias = ""
keyPassword = "__unset" // And these must be set to non-empty in order to have the signing step added to the task graph.
storeFile = null
storePassword = "__unset"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
addSigningProps(cdvReleaseSigningPropertiesFile, signingConfigs.release)
}
if (cdvDebugSigningPropertiesFile) {
addSigningProps(cdvDebugSigningPropertiesFile, signingConfigs.debug)
}
}
dependencies {
implementation fileTree(dir: 'libs', include: '*.jar')
// SUB-PROJECT DEPENDENCIES START
// SUB-PROJECT DEPENDENCIES END
}
def promptForReleaseKeyPassword() {
if (!cdvReleaseSigningPropertiesFile) {
return;
}
if ('__unset'.equals(android.signingConfigs.release.storePassword)) {
android.signingConfigs.release.storePassword = privateHelpers.promptForPassword('Enter key store password: ')
}
if ('__unset'.equals(android.signingConfigs.release.keyPassword)) {
android.signingConfigs.release.keyPassword = privateHelpers.promptForPassword('Enter key password: ');
}
}
gradle.taskGraph.whenReady { taskGraph ->
taskGraph.getAllTasks().each() { task ->
if (task.name == 'validateReleaseSigning' || task.name == 'validateSigningRelease') {
promptForReleaseKeyPassword()
}
}
}
def addSigningProps(propsFilePath, signingConfig) {
def propsFile = file(propsFilePath)
def props = new Properties()
propsFile.withReader { reader ->
props.load(reader)
}
def storeFile = new File(props.get('key.store') ?: privateHelpers.ensureValueExists(propsFilePath, props, 'storeFile'))
if (!storeFile.isAbsolute()) {
storeFile = RelativePath.parse(true, storeFile.toString()).getFile(propsFile.getParentFile())
}
if (!storeFile.exists()) {
throw new FileNotFoundException('Keystore file does not exist: ' + storeFile.getAbsolutePath())
}
signingConfig.keyAlias = props.get('key.alias') ?: privateHelpers.ensureValueExists(propsFilePath, props, 'keyAlias')
signingConfig.keyPassword = props.get('keyPassword', props.get('key.alias.password', signingConfig.keyPassword))
signingConfig.storeFile = storeFile
signingConfig.storePassword = props.get('storePassword', props.get('key.store.password', signingConfig.storePassword))
def storeType = props.get('storeType', props.get('key.store.type', ''))
if (!storeType) {
def filename = storeFile.getName().toLowerCase();
if (filename.endsWith('.p12') || filename.endsWith('.pfx')) {
storeType = 'pkcs12'
} else {
storeType = signingConfig.storeType // "jks"
}
}
signingConfig.storeType = storeType
}
for (def func : cdvPluginPostBuildExtras) {
func()
}
// This can be defined within build-extras.gradle as:
// ext.postBuildExtras = { ... code here ... }
if (hasProperty('postBuildExtras')) {
postBuildExtras()
}

View File

@@ -21,10 +21,10 @@
var path = require('path');
var Api = require('./templates/cordova/Api');
var args = require('nopt')({
'link': Boolean,
'shared': Boolean,
'help': Boolean
}, { 'd': '--verbose' });
link: Boolean,
shared: Boolean,
help: Boolean
}, { d: '--verbose' });
if (args.help || args.argv.remain.length === 0) {
console.log('Usage: ' + path.relative(process.cwd(), path.join(__dirname, 'update')) + ' <path_to_project> [--link]');
@@ -34,4 +34,7 @@ if (args.help || args.argv.remain.length === 0) {
require('./templates/cordova/loggingHelper').adjustLoggerLevel(args);
Api.updatePlatform(args.argv.remain[0], { link: (args.link || args.shared) }).done();
Api.updatePlatform(args.argv.remain[0], { link: (args.link || args.shared) }).catch(err => {
console.error(err);
process.exitCode = 1;
});

View File

@@ -5,9 +5,9 @@
:: 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

View File

@@ -25,12 +25,12 @@ var nativeApi = this._cordovaNative || require('cordova/android/promptbasednativ
var currentApi = nativeApi;
module.exports = {
get: function() { return currentApi; },
setPreferPrompt: function(value) {
get: function () { return currentApi; },
setPreferPrompt: function (value) {
currentApi = value ? require('cordova/android/promptbasednativeapi') : nativeApi;
},
// Used only by tests.
set: function(value) {
set: function (value) {
currentApi = value;
}
};

View File

@@ -23,13 +23,13 @@
*/
module.exports = {
exec: function(bridgeSecret, service, action, callbackId, argsJson) {
return prompt(argsJson, 'gap:'+JSON.stringify([bridgeSecret, service, action, callbackId]));
exec: function (bridgeSecret, service, action, callbackId, argsJson) {
return prompt(argsJson, 'gap:' + JSON.stringify([bridgeSecret, service, action, callbackId]));
},
setNativeToJsBridgeMode: function(bridgeSecret, value) {
setNativeToJsBridgeMode: function (bridgeSecret, value) {
prompt(value, 'gap_bridge_mode:' + bridgeSecret);
},
retrieveJsMessages: function(bridgeSecret, fromOnlineEvent) {
retrieveJsMessages: function (bridgeSecret, fromOnlineEvent) {
return prompt(+fromOnlineEvent, 'gap_poll:' + bridgeSecret);
}
};

133
cordova-js-src/exec.js vendored
View File

@@ -33,38 +33,38 @@
* @param {String} action Action to be run in cordova
* @param {String[]} [args] Zero or more arguments to pass to the method
*/
var cordova = require('cordova'),
nativeApiProvider = require('cordova/android/nativeapiprovider'),
utils = require('cordova/utils'),
base64 = require('cordova/base64'),
channel = require('cordova/channel'),
jsToNativeModes = {
PROMPT: 0,
JS_OBJECT: 1
},
nativeToJsModes = {
// Polls for messages using the JS->Native bridge.
POLLING: 0,
// For LOAD_URL to be viable, it would need to have a work-around for
// the bug where the soft-keyboard gets dismissed when a message is sent.
LOAD_URL: 1,
// For the ONLINE_EVENT to be viable, it would need to intercept all event
// listeners (both through addEventListener and window.ononline) as well
// as set the navigator property itself.
ONLINE_EVENT: 2,
EVAL_BRIDGE: 3
},
jsToNativeBridgeMode, // Set lazily.
nativeToJsBridgeMode = nativeToJsModes.EVAL_BRIDGE,
pollEnabled = false,
bridgeSecret = -1;
var cordova = require('cordova');
var nativeApiProvider = require('cordova/android/nativeapiprovider');
var utils = require('cordova/utils');
var base64 = require('cordova/base64');
var channel = require('cordova/channel');
var jsToNativeModes = {
PROMPT: 0,
JS_OBJECT: 1
};
var nativeToJsModes = {
// Polls for messages using the JS->Native bridge.
POLLING: 0,
// For LOAD_URL to be viable, it would need to have a work-around for
// the bug where the soft-keyboard gets dismissed when a message is sent.
LOAD_URL: 1,
// For the ONLINE_EVENT to be viable, it would need to intercept all event
// listeners (both through addEventListener and window.ononline) as well
// as set the navigator property itself.
ONLINE_EVENT: 2,
EVAL_BRIDGE: 3
};
var jsToNativeBridgeMode; // Set lazily.
var nativeToJsBridgeMode = nativeToJsModes.EVAL_BRIDGE;
var pollEnabled = false;
var bridgeSecret = -1;
var messagesFromNative = [];
var isProcessing = false;
var resolvedPromise = typeof Promise == 'undefined' ? null : Promise.resolve();
var nextTick = resolvedPromise ? function(fn) { resolvedPromise.then(fn); } : function(fn) { setTimeout(fn); };
var resolvedPromise = typeof Promise === 'undefined' ? null : Promise.resolve();
var nextTick = resolvedPromise ? function (fn) { resolvedPromise.then(fn); } : function (fn) { setTimeout(fn); };
function androidExec(success, fail, service, action, args) {
function androidExec (success, fail, service, action, args) {
if (bridgeSecret < 0) {
// If we ever catch this firing, we'll need to queue up exec()s
// and fire them once we get a secret. For now, I don't think
@@ -83,21 +83,21 @@ function androidExec(success, fail, service, action, args) {
// Process any ArrayBuffers in the args into a string.
for (var i = 0; i < args.length; i++) {
if (utils.typeName(args[i]) == 'ArrayBuffer') {
if (utils.typeName(args[i]) === 'ArrayBuffer') {
args[i] = base64.fromArrayBuffer(args[i]);
}
}
var callbackId = service + cordova.callbackId++,
argsJson = JSON.stringify(args);
var callbackId = service + cordova.callbackId++;
var argsJson = JSON.stringify(args);
if (success || fail) {
cordova.callbacks[callbackId] = {success:success, fail:fail};
cordova.callbacks[callbackId] = { success: success, fail: fail };
}
var msgs = nativeApiProvider.get().exec(bridgeSecret, service, action, callbackId, argsJson);
// If argsJson was received by Java as null, try again with the PROMPT bridge mode.
// This happens in rare circumstances, such as when certain Unicode characters are passed over the bridge on a Galaxy S2. See CB-2666.
if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT && msgs === "@Null arguments.") {
if (jsToNativeBridgeMode === jsToNativeModes.JS_OBJECT && msgs === '@Null arguments.') {
androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT);
androidExec(success, fail, service, action, args);
androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
@@ -108,16 +108,16 @@ function androidExec(success, fail, service, action, args) {
}
}
androidExec.init = function() {
androidExec.init = function () {
bridgeSecret = +prompt('', 'gap_init:' + nativeToJsBridgeMode);
channel.onNativeReady.fire();
};
function pollOnceFromOnlineEvent() {
function pollOnceFromOnlineEvent () {
pollOnce(true);
}
function pollOnce(opt_fromOnlineEvent) {
function pollOnce (opt_fromOnlineEvent) {
if (bridgeSecret < 0) {
// This can happen when the NativeToJsMessageQueue resets the online state on page transitions.
// We know there's nothing to retrieve, so no need to poll.
@@ -131,15 +131,15 @@ function pollOnce(opt_fromOnlineEvent) {
}
}
function pollingTimerFunc() {
function pollingTimerFunc () {
if (pollEnabled) {
pollOnce();
setTimeout(pollingTimerFunc, 50);
}
}
function hookOnlineApis() {
function proxyEvent(e) {
function hookOnlineApis () {
function proxyEvent (e) {
cordova.fireWindowEvent(e.type);
}
// The network module takes care of firing online and offline events.
@@ -159,19 +159,19 @@ hookOnlineApis();
androidExec.jsToNativeModes = jsToNativeModes;
androidExec.nativeToJsModes = nativeToJsModes;
androidExec.setJsToNativeBridgeMode = function(mode) {
if (mode == jsToNativeModes.JS_OBJECT && !window._cordovaNative) {
androidExec.setJsToNativeBridgeMode = function (mode) {
if (mode === jsToNativeModes.JS_OBJECT && !window._cordovaNative) {
mode = jsToNativeModes.PROMPT;
}
nativeApiProvider.setPreferPrompt(mode == jsToNativeModes.PROMPT);
nativeApiProvider.setPreferPrompt(mode === jsToNativeModes.PROMPT);
jsToNativeBridgeMode = mode;
};
androidExec.setNativeToJsBridgeMode = function(mode) {
if (mode == nativeToJsBridgeMode) {
androidExec.setNativeToJsBridgeMode = function (mode) {
if (mode === nativeToJsBridgeMode) {
return;
}
if (nativeToJsBridgeMode == nativeToJsModes.POLLING) {
if (nativeToJsBridgeMode === nativeToJsModes.POLLING) {
pollEnabled = false;
}
@@ -182,32 +182,32 @@ androidExec.setNativeToJsBridgeMode = function(mode) {
nativeApiProvider.get().setNativeToJsBridgeMode(bridgeSecret, mode);
}
if (mode == nativeToJsModes.POLLING) {
if (mode === nativeToJsModes.POLLING) {
pollEnabled = true;
setTimeout(pollingTimerFunc, 1);
}
};
function buildPayload(payload, message) {
function buildPayload (payload, message) {
var payloadKind = message.charAt(0);
if (payloadKind == 's') {
if (payloadKind === 's') {
payload.push(message.slice(1));
} else if (payloadKind == 't') {
} else if (payloadKind === 't') {
payload.push(true);
} else if (payloadKind == 'f') {
} else if (payloadKind === 'f') {
payload.push(false);
} else if (payloadKind == 'N') {
} else if (payloadKind === 'N') {
payload.push(null);
} else if (payloadKind == 'n') {
} else if (payloadKind === 'n') {
payload.push(+message.slice(1));
} else if (payloadKind == 'A') {
} else if (payloadKind === 'A') {
var data = message.slice(1);
payload.push(base64.toArrayBuffer(data));
} else if (payloadKind == 'S') {
} else if (payloadKind === 'S') {
payload.push(window.atob(message.slice(1)));
} else if (payloadKind == 'M') {
} else if (payloadKind === 'M') {
var multipartMessages = message.slice(1);
while (multipartMessages !== "") {
while (multipartMessages !== '') {
var spaceIdx = multipartMessages.indexOf(' ');
var msgLen = +multipartMessages.slice(0, spaceIdx);
var multipartMessage = multipartMessages.substr(spaceIdx + 1, msgLen);
@@ -220,14 +220,15 @@ function buildPayload(payload, message) {
}
// Processes a single message, as encoded by NativeToJsMessageQueue.java.
function processMessage(message) {
function processMessage (message) {
var firstChar = message.charAt(0);
if (firstChar == 'J') {
if (firstChar === 'J') {
// This is deprecated on the .java side. It doesn't work with CSP enabled.
// eslint-disable-next-line no-eval
eval(message.slice(1));
} else if (firstChar == 'S' || firstChar == 'F') {
var success = firstChar == 'S';
var keepCallback = message.charAt(1) == '1';
} else if (firstChar === 'S' || firstChar === 'F') {
var success = firstChar === 'S';
var keepCallback = message.charAt(1) === '1';
var spaceIdx = message.indexOf(' ', 2);
var status = +message.slice(2, spaceIdx);
var nextSpaceIdx = message.indexOf(' ', spaceIdx + 1);
@@ -237,11 +238,11 @@ function processMessage(message) {
buildPayload(payload, payloadMessage);
cordova.callbackFromNative(callbackId, success, status, payload, keepCallback);
} else {
console.log("processMessage failed: invalid message: " + JSON.stringify(message));
console.log('processMessage failed: invalid message: ' + JSON.stringify(message));
}
}
function processMessages() {
function processMessages () {
// Check for the reentrant case.
if (isProcessing) {
return;
@@ -254,7 +255,7 @@ function processMessages() {
var msg = popMessageFromQueue();
// The Java side can send a * message to indicate that it
// still has messages waiting to be retrieved.
if (msg == '*' && messagesFromNative.length === 0) {
if (msg === '*' && messagesFromNative.length === 0) {
nextTick(pollOnce);
return;
}
@@ -267,9 +268,9 @@ function processMessages() {
}
}
function popMessageFromQueue() {
function popMessageFromQueue () {
var messageBatch = messagesFromNative.shift();
if (messageBatch == '*') {
if (messageBatch === '*') {
return '*';
}

View File

@@ -24,11 +24,11 @@ var lastResumeEvent = null;
module.exports = {
id: 'android',
bootstrap: function() {
var channel = require('cordova/channel'),
cordova = require('cordova'),
exec = require('cordova/exec'),
modulemapper = require('cordova/modulemapper');
bootstrap: function () {
var channel = require('cordova/channel');
var cordova = require('cordova');
var exec = require('cordova/exec');
var modulemapper = require('cordova/modulemapper');
// Get the shared secret needed to use the bridge.
exec.init();
@@ -40,21 +40,21 @@ module.exports = {
// Inject a listener for the backbutton on the document.
var backButtonChannel = cordova.addDocumentEventHandler('backbutton');
backButtonChannel.onHasSubscribersChange = function() {
backButtonChannel.onHasSubscribersChange = function () {
// If we just attached the first handler or detached the last handler,
// let native know we need to override the back button.
exec(null, null, APP_PLUGIN_NAME, "overrideBackbutton", [this.numHandlers == 1]);
exec(null, null, APP_PLUGIN_NAME, 'overrideBackbutton', [this.numHandlers === 1]);
};
// Add hardware MENU and SEARCH button handlers
cordova.addDocumentEventHandler('menubutton');
cordova.addDocumentEventHandler('searchbutton');
function bindButtonChannel(buttonName) {
function bindButtonChannel (buttonName) {
// generic button bind used for volumeup/volumedown buttons
var volumeButtonChannel = cordova.addDocumentEventHandler(buttonName + 'button');
volumeButtonChannel.onHasSubscribersChange = function() {
exec(null, null, APP_PLUGIN_NAME, "overrideButton", [buttonName, this.numHandlers == 1]);
volumeButtonChannel.onHasSubscribersChange = function () {
exec(null, null, APP_PLUGIN_NAME, 'overrideButton', [buttonName, this.numHandlers === 1]);
};
}
// Inject a listener for the volume buttons on the document.
@@ -66,7 +66,7 @@ module.exports = {
// plugin result is delivered even after the event is fired (CB-10498)
var cordovaAddEventListener = document.addEventListener;
document.addEventListener = function(evt, handler, capture) {
document.addEventListener = function (evt, handler, capture) {
cordovaAddEventListener(evt, handler, capture);
if (evt === 'resume' && lastResumeEvent) {
@@ -76,50 +76,47 @@ module.exports = {
// Let native code know we are all done on the JS side.
// Native code will then un-hide the WebView.
channel.onCordovaReady.subscribe(function() {
channel.onCordovaReady.subscribe(function () {
exec(onMessageFromNative, null, APP_PLUGIN_NAME, 'messageChannel', []);
exec(null, null, APP_PLUGIN_NAME, "show", []);
exec(null, null, APP_PLUGIN_NAME, 'show', []);
});
}
};
function onMessageFromNative(msg) {
function onMessageFromNative (msg) {
var cordova = require('cordova');
var action = msg.action;
switch (action)
{
// Button events
case 'backbutton':
case 'menubutton':
case 'searchbutton':
// App life cycle events
case 'pause':
// Volume events
case 'volumedownbutton':
case 'volumeupbutton':
cordova.fireDocumentEvent(action);
break;
case 'resume':
if(arguments.length > 1 && msg.pendingResult) {
if(arguments.length === 2) {
msg.pendingResult.result = arguments[1];
} else {
// The plugin returned a multipart message
var res = [];
for(var i = 1; i < arguments.length; i++) {
res.push(arguments[i]);
}
msg.pendingResult.result = res;
switch (action) {
// pause and resume are Android app life cycle events
case 'backbutton':
case 'menubutton':
case 'searchbutton':
case 'pause':
case 'volumedownbutton':
case 'volumeupbutton':
cordova.fireDocumentEvent(action);
break;
case 'resume':
if (arguments.length > 1 && msg.pendingResult) {
if (arguments.length === 2) {
msg.pendingResult.result = arguments[1];
} else {
// The plugin returned a multipart message
var res = [];
for (var i = 1; i < arguments.length; i++) {
res.push(arguments[i]);
}
// Save the plugin result so that it can be delivered to the js
// even if they miss the initial firing of the event
lastResumeEvent = msg;
msg.pendingResult.result = res;
}
cordova.fireDocumentEvent(action, msg);
break;
default:
throw new Error('Unknown event action ' + action);
// Save the plugin result so that it can be delivered to the js
// even if they miss the initial firing of the event
lastResumeEvent = msg;
}
cordova.fireDocumentEvent(action, msg);
break;
default:
throw new Error('Unknown event action ' + action);
}
}

View File

@@ -26,8 +26,8 @@ module.exports = {
/**
* Clear the resource cache.
*/
clearCache:function() {
exec(null, null, APP_PLUGIN_NAME, "clearCache", []);
clearCache: function () {
exec(null, null, APP_PLUGIN_NAME, 'clearCache', []);
},
/**
@@ -44,31 +44,31 @@ module.exports = {
* Example:
* navigator.app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000});
*/
loadUrl:function(url, props) {
exec(null, null, APP_PLUGIN_NAME, "loadUrl", [url, props]);
loadUrl: function (url, props) {
exec(null, null, APP_PLUGIN_NAME, 'loadUrl', [url, props]);
},
/**
* Cancel loadUrl that is waiting to be loaded.
*/
cancelLoadUrl:function() {
exec(null, null, APP_PLUGIN_NAME, "cancelLoadUrl", []);
cancelLoadUrl: function () {
exec(null, null, APP_PLUGIN_NAME, 'cancelLoadUrl', []);
},
/**
* Clear web history in this web view.
* Instead of BACK button loading the previous web page, it will exit the app.
*/
clearHistory:function() {
exec(null, null, APP_PLUGIN_NAME, "clearHistory", []);
clearHistory: function () {
exec(null, null, APP_PLUGIN_NAME, 'clearHistory', []);
},
/**
* Go to previous page displayed.
* This is the same as pressing the backbutton on Android device.
*/
backHistory:function() {
exec(null, null, APP_PLUGIN_NAME, "backHistory", []);
backHistory: function () {
exec(null, null, APP_PLUGIN_NAME, 'backHistory', []);
},
/**
@@ -80,8 +80,8 @@ module.exports = {
*
* @param override T=override, F=cancel override
*/
overrideBackbutton:function(override) {
exec(null, null, APP_PLUGIN_NAME, "overrideBackbutton", [override]);
overrideBackbutton: function (override) {
exec(null, null, APP_PLUGIN_NAME, 'overrideBackbutton', [override]);
},
/**
@@ -95,14 +95,14 @@ module.exports = {
* @param button volumeup, volumedown
* @param override T=override, F=cancel override
*/
overrideButton:function(button, override) {
exec(null, null, APP_PLUGIN_NAME, "overrideButton", [button, override]);
overrideButton: function (button, override) {
exec(null, null, APP_PLUGIN_NAME, 'overrideButton', [button, override]);
},
/**
* Exit and terminate the application.
*/
exitApp:function() {
return exec(null, null, APP_PLUGIN_NAME, "exitApp", []);
exitApp: function () {
return exec(null, null, APP_PLUGIN_NAME, 'exitApp', []);
}
};

View File

@@ -1,9 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="output" path="bin/classes"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>

View File

@@ -1,33 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>Cordova</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@@ -31,7 +31,7 @@ buildscript {
dependencies {
// The gradle plugin and the maven plugin have to be updated after each version of Android
// studio comes out
classpath 'com.android.tools.build:gradle:3.3.0'
classpath 'com.android.tools.build:gradle:4.0.0'
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3'
}
@@ -49,7 +49,7 @@ apply plugin: 'com.github.dcendents.android-maven'
apply plugin: 'com.jfrog.bintray'
group = 'org.apache.cordova'
version = '8.1.0-dev'
version = '9.0.0'
android {
compileSdkVersion cdvCompileSdkVersion
@@ -62,7 +62,7 @@ android {
// For the Android Cordova Lib, we will hardcode the minSdkVersion and not allow changes.
defaultConfig {
minSdkVersion 19
minSdkVersion 22
}
sourceSets {
@@ -140,9 +140,9 @@ bintray {
licenses = ['Apache-2.0']
labels = ['android', 'cordova', 'phonegap']
version {
name = '8.1.0-dev'
name = '9.0.0'
released = new Date()
vcsTag = '8.1.0-dev'
vcsTag = '9.0.0'
}
}
}

View File

@@ -31,10 +31,10 @@
<fail message="The required minimum version of ant is 1.8.0, you have ${ant.version}"
unless="thisantversion" />
<!-- The local.properties file is created and updated by the 'android'
<!-- The local.properties file is created and updated by the 'android'
tool. (For example "sdkdir/tools/android update project -p ." inside
of this directory where the AndroidManifest.xml file exists. This
properties file that gets built contains the path to the SDK. It
properties file that gets built contains the path to the SDK. It
should *NOT* be checked into Version Control Systems since it holds
data about the local machine. -->
<available file="local.properties" property="exists.local.properties" />
@@ -132,11 +132,11 @@
excludes="org/apache/cordova/R.class,org/apache/cordova/R$*.class"
jarfile="cordova-${version}.jar" />
</target>
<target name="javadoc">
<delete dir="javadoc-public" failonerror="false" />
<javadoc
access="public"
access="public"
destdir="javadoc-public"
classpath="${sdk.dir}/platforms/${target}/android.jar">
<packageset dir="src">
@@ -145,7 +145,7 @@
</javadoc>
<delete dir="javadoc-private" failonerror="false" />
<javadoc
access="private"
access="private"
destdir="javadoc-private"
classpath="${sdk.dir}/platforms/${target}/android.jar">
<packageset dir="src">

View File

@@ -19,6 +19,7 @@
import java.util.regex.Pattern
import groovy.swing.SwingBuilder
import com.g00fy2.versioncompare.Version
String doEnsureValueExists(filePath, props, key) {
if (props.get(key) == null) {
@@ -39,62 +40,52 @@ String doGetProjectTarget() {
return doEnsureValueExists('project.properties', props, 'target')
}
String[] getAvailableBuildTools() {
def buildToolsDir = new File(getAndroidSdkDir(), "build-tools")
buildToolsDir.list()
.findAll { it ==~ /[0-9.]+/ }
.sort { a, b -> compareVersions(b, a) }
Boolean isVersionValid(version) {
return !(new Version(version)).isEqual('0.0.0')
}
String doFindLatestInstalledBuildTools(String minBuildToolsVersion) {
def availableBuildToolsVersions
String doFindLatestInstalledBuildTools(String minBuildToolsVersionString) {
def buildToolsDirContents
try {
availableBuildToolsVersions = getAvailableBuildTools()
def buildToolsDir = new File(getAndroidSdkDir(), "build-tools")
buildToolsDirContents = buildToolsDir.list()
} catch (e) {
println "An exception occurred while trying to find the Android build tools."
throw e
}
if (availableBuildToolsVersions.length > 0) {
def highestBuildToolsVersion = availableBuildToolsVersions[0]
if (compareVersions(highestBuildToolsVersion, minBuildToolsVersion) < 0) {
throw new RuntimeException(
"No usable Android build tools found. Highest installed version is " +
highestBuildToolsVersion + "; minimum version required is " +
minBuildToolsVersion + ".")
}
highestBuildToolsVersion
} else {
throw new RuntimeException(
"No installed build tools found. Install the Android build tools version " +
minBuildToolsVersion + " or higher.")
def highestBuildToolsVersion = buildToolsDirContents
.collect { new Version(it) }
// Invalid inputs will be handled as 0.0.0
.findAll { it.isHigherThan('0.0.0') }
.max()
if (highestBuildToolsVersion == null) {
throw new RuntimeException("""
No installed build tools found. Install the Android build tools
version ${minBuildToolsVersionString} or higher.
""".replaceAll(/\s+/, ' ').trim())
}
}
// Return the first non-zero result of subtracting version list elements
// pairwise. If they are all identical, return the difference in length of
// the two lists.
int compareVersionList(Collection aParts, Collection bParts) {
def pairs = ([aParts, bParts]).transpose()
pairs.findResult(aParts.size()-bParts.size()) {it[0] - it[1] != 0 ? it[0] - it[1] : null}
}
if (highestBuildToolsVersion.isLowerThan(minBuildToolsVersionString)) {
throw new RuntimeException("""
No usable Android build tools found. Highest installed version is
${highestBuildToolsVersion.getOriginalString()}; minimum version
required is ${minBuildToolsVersionString}.
""".replaceAll(/\s+/, ' ').trim())
}
// Compare two version strings, such as "19.0.0" and "18.1.1.0". If all matched
// elements are identical, the longer version is the largest by this method.
// Examples:
// "19.0.0" > "19"
// "19.0.1" > "19.0.0"
// "19.1.0" > "19.0.1"
// "19" > "18.999.999"
int compareVersions(String a, String b) {
def aParts = a.tokenize('.').collect {it.toInteger()}
def bParts = b.tokenize('.').collect {it.toInteger()}
compareVersionList(aParts, bParts)
highestBuildToolsVersion.getOriginalString()
}
String getAndroidSdkDir() {
def rootDir = project.rootDir
def androidSdkDir = null
String envVar = System.getenv("ANDROID_HOME")
String envVar = System.getenv("ANDROID_SDK_ROOT")
if (envVar == null) {
envVar = System.getenv("ANDROID_HOME")
}
def localProperties = new File(rootDir, 'local.properties')
String systemProperty = System.getProperty("android.home")
if (envVar != null) {
@@ -203,3 +194,12 @@ ext {
cdvHelpers.getConfigPreference = { name, defaultValue -> doGetConfigPreference(name, defaultValue) }
}
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.g00fy2:versioncompare:1.3.4@jar'
}
}

View File

@@ -5,7 +5,7 @@
split.density=false
# Project target.
target=android-28
target=android-29
apk-configurations=
renderscript.opt.level=O0
android.library=true

View File

@@ -51,7 +51,7 @@ public class BuildHelper {
{
try
{
Class<?> clazz = Class.forName(ctx.getPackageName() + ".BuildConfig");
Class<?> clazz = Class.forName(ctx.getClass().getPackage().getName() + ".BuildConfig");
Field field = clazz.getField(key);
return field.get(null);
} catch (ClassNotFoundException e) {
@@ -62,6 +62,9 @@ public class BuildHelper {
} catch (IllegalAccessException e) {
LOG.d(TAG, "Illegal Access Exception: Let's print a stack trace.");
e.printStackTrace();
} catch (NullPointerException e) {
LOG.d(TAG, "Null Pointer Exception: Let's print a stack trace.");
e.printStackTrace();
}
return null;

View File

@@ -127,7 +127,7 @@ public class CordovaBridge {
}
public String promptOnJsPrompt(String origin, String message, String defaultValue) {
if (defaultValue != null && defaultValue.length() > 3 && defaultValue.startsWith("gap:")) {
if (defaultValue != null && defaultValue.startsWith("gap:")) {
JSONArray array;
try {
array = new JSONArray(defaultValue.substring(4));

View File

@@ -36,7 +36,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
public CordovaClientCertRequest(ClientCertRequest request) {
this.request = request;
}
/**
* Cancel this request
*/
@@ -45,7 +45,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
{
request.cancel();
}
/*
* Returns the host name of the server requesting the certificate.
*/
@@ -54,7 +54,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
{
return request.getHost();
}
/*
* Returns the acceptable types of asymmetric keys (can be null).
*/
@@ -63,7 +63,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
{
return request.getKeyTypes();
}
/*
* Returns the port number of the server requesting the certificate.
*/
@@ -72,7 +72,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
{
return request.getPort();
}
/*
* Returns the acceptable certificate issuers for the certificate matching the private key (can be null).
*/
@@ -81,7 +81,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
{
return request.getPrincipals();
}
/*
* Ignore the request for now. Do not remember user's choice.
*/
@@ -90,12 +90,12 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
{
request.ignore();
}
/*
* Proceed with the specified private key and client certificate chain. Remember the user's positive choice and use it for future requests.
*
*
* @param privateKey The privateKey
* @param chain The certificate chain
* @param chain The certificate chain
*/
@SuppressLint("NewApi")
public void proceed(PrivateKey privateKey, X509Certificate[] chain)

View File

@@ -31,17 +31,17 @@ public class CordovaHttpAuthHandler implements ICordovaHttpAuthHandler {
public CordovaHttpAuthHandler(HttpAuthHandler handler) {
this.handler = handler;
}
/**
* Instructs the WebView to cancel the authentication request.
*/
public void cancel () {
this.handler.cancel();
}
/**
* Instructs the WebView to proceed with the authentication with the given credentials.
*
*
* @param username
* @param password
*/

View File

@@ -73,7 +73,7 @@ public interface CordovaInterface {
* @return Object or null
*/
public Object onMessage(String id, Object data);
/**
* Returns a shared thread pool that can be used for background tasks.
*/

View File

@@ -47,11 +47,11 @@ public class CordovaPreferences {
public void set(String name, int value) {
set(name, "" + value);
}
public void set(String name, double value) {
set(name, "" + value);
}
public Map<String, String> getAll() {
return prefs;
}

View File

@@ -77,7 +77,7 @@ public class CordovaResourceApi {
public static final String PLUGIN_URI_SCHEME = "cdvplugin";
private static final String[] LOCAL_FILE_PROJECTION = { "_data" };
public static Thread jsThread;
private final AssetManager assetManager;
@@ -91,7 +91,7 @@ public class CordovaResourceApi {
this.assetManager = context.getAssets();
this.pluginManager = pluginManager;
}
public void setThreadCheckingEnabled(boolean value) {
threadCheckingEnabled = value;
}
@@ -99,8 +99,8 @@ public class CordovaResourceApi {
public boolean isThreadCheckingEnabled() {
return threadCheckingEnabled;
}
public static int getUriType(Uri uri) {
assertNonRelative(uri);
String scheme = uri.getScheme();
@@ -130,7 +130,7 @@ public class CordovaResourceApi {
}
return URI_TYPE_UNKNOWN;
}
public Uri remapUri(Uri uri) {
assertNonRelative(uri);
Uri pluginUri = pluginManager.remapUri(uri);
@@ -140,7 +140,7 @@ public class CordovaResourceApi {
public String remapPath(String path) {
return remapUri(Uri.fromFile(new File(path))).getPath();
}
/**
* Returns a File that points to the resource, or null if the resource
* is not on the local filesystem.
@@ -170,7 +170,7 @@ public class CordovaResourceApi {
}
return null;
}
public String getMimeType(Uri uri) {
switch (getUriType(uri)) {
case URI_TYPE_FILE:
@@ -197,11 +197,11 @@ public class CordovaResourceApi {
}
}
}
return null;
}
//This already exists
private String getMimeTypeFromPath(String path) {
String extension = path;
@@ -219,7 +219,7 @@ public class CordovaResourceApi {
}
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
/**
* Opens a stream to the given URI, also providing the MIME type & length.
* @return Never returns null.
@@ -342,7 +342,7 @@ public class CordovaResourceApi {
assertBackgroundThread();
return (HttpURLConnection)new URL(uri.toString()).openConnection();
}
// Copies the input to the output in the most efficient manner possible.
// Closes both streams.
public void copyResource(OpenForReadResult input, OutputStream outputStream) throws IOException {
@@ -364,16 +364,16 @@ public class CordovaResourceApi {
} else {
final int BUFFER_SIZE = 8192;
byte[] buffer = new byte[BUFFER_SIZE];
for (;;) {
int bytesRead = inputStream.read(buffer, 0, BUFFER_SIZE);
if (bytesRead <= 0) {
break;
}
outputStream.write(buffer, 0, bytesRead);
}
}
}
} finally {
input.inputStream.close();
if (outputStream != null) {
@@ -390,7 +390,7 @@ public class CordovaResourceApi {
public void copyResource(Uri sourceUri, Uri dstUri) throws IOException {
copyResource(openForRead(sourceUri), openOutputStream(dstUri));
}
private void assertBackgroundThread() {
if (threadCheckingEnabled) {
Thread curThread = Thread.currentThread();
@@ -402,7 +402,7 @@ public class CordovaResourceApi {
}
}
}
private String getDataUriMimeType(Uri uri) {
String uriAsString = uri.getSchemeSpecificPart();
int commaPos = uriAsString.indexOf(',');
@@ -447,20 +447,20 @@ public class CordovaResourceApi {
InputStream inputStream = new ByteArrayInputStream(data);
return new OpenForReadResult(uri, inputStream, contentType, data.length, null);
}
private static void assertNonRelative(Uri uri) {
if (!uri.isAbsolute()) {
throw new IllegalArgumentException("Relative URIs are not supported.");
}
}
public static final class OpenForReadResult {
public final Uri uri;
public final InputStream inputStream;
public final String mimeType;
public final long length;
public final AssetFileDescriptor assetFd;
public OpenForReadResult(Uri uri, InputStream inputStream, String mimeType, long length, AssetFileDescriptor assetFd) {
this.uri = uri;
this.inputStream = inputStream;

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

View File

@@ -244,6 +244,21 @@ public class CordovaWebViewImpl implements CordovaWebView {
}
}
private static class WrapperView extends FrameLayout {
private final CordovaWebViewEngine engine;
public WrapperView(Context context, CordovaWebViewEngine engine) {
super(context);
this.engine = engine;
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
return engine.getView().dispatchKeyEvent(event);
}
}
@Override
@Deprecated
public void showCustomView(View view, WebChromeClient.CustomViewCallback callback) {
@@ -255,13 +270,16 @@ public class CordovaWebViewImpl implements CordovaWebView {
return;
}
WrapperView wrapperView = new WrapperView(getContext(), engine);
wrapperView.addView(view);
// Store the view and its callback for later (to kill it properly)
mCustomView = view;
mCustomView = wrapperView;
mCustomViewCallback = callback;
// Add the custom view to its container.
ViewGroup parent = (ViewGroup) engine.getView().getParent();
parent.addView(view, new FrameLayout.LayoutParams(
parent.addView(wrapperView, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
Gravity.CENTER));

View File

@@ -389,7 +389,7 @@ public class CoreAndroid extends CordovaPlugin {
{
try
{
Class<?> clazz = Class.forName(ctx.getPackageName() + ".BuildConfig");
Class<?> clazz = Class.forName(ctx.getClass().getPackage().getName() + ".BuildConfig");
Field field = clazz.getField(key);
return field.get(null);
} catch (ClassNotFoundException e) {
@@ -400,6 +400,9 @@ public class CoreAndroid extends CordovaPlugin {
} catch (IllegalAccessException e) {
LOG.d(TAG, "Illegal Access Exception: Let's print a stack trace.");
e.printStackTrace();
} catch (NullPointerException e) {
LOG.d(TAG, "Null Pointer Exception: Let's print a stack trace.");
e.printStackTrace();
}
return null;

View File

@@ -30,37 +30,37 @@ public interface ICordovaClientCertRequest {
* Cancel this request
*/
public void cancel();
/*
* Returns the host name of the server requesting the certificate.
*/
public String getHost();
/*
* Returns the acceptable types of asymmetric keys (can be null).
*/
public String[] getKeyTypes();
/*
* Returns the port number of the server requesting the certificate.
*/
public int getPort();
/*
* Returns the acceptable certificate issuers for the certificate matching the private key (can be null).
*/
public Principal[] getPrincipals();
/*
* Ignore the request for now. Do not remember user's choice.
*/
public void ignore();
/*
* Proceed with the specified private key and client certificate chain. Remember the user's positive choice and use it for future requests.
*
*
* @param privateKey The privateKey
* @param chain The certificate chain
* @param chain The certificate chain
*/
public void proceed(PrivateKey privateKey, X509Certificate[] chain);
}

View File

@@ -27,10 +27,10 @@ package org.apache.cordova;
* Instructs the WebView to cancel the authentication request.
*/
public void cancel ();
/**
* Instructs the WebView to proceed with the authentication with the given credentials.
*
*
* @param username The user name
* @param password The password
*/

View File

@@ -36,10 +36,11 @@ public class NativeToJsMessageQueue {
// exec() is asynchronous. Set this to true when running bridge benchmarks.
static final boolean DISABLE_EXEC_CHAINING = false;
// Arbitrarily chosen upper limit for how much data to send to JS in one shot.
// This currently only chops up on message boundaries. It may be useful
// to allow it to break up messages.
private static int MAX_PAYLOAD_SIZE = 50 * 1024 * 10240;
// A hopefully reasonable upper limit of how much combined payload data
// to send to the JavaScript in one shot.
// This currently only chops up on message boundaries.
// It may be useful to split and reassemble response messages someday.
private static int COMBINED_RESPONSE_CUTOFF = 16 * 1024 * 1024;
/**
* When true, the active listener is not fired upon enqueue. When set to false,
@@ -124,7 +125,10 @@ public class NativeToJsMessageQueue {
/**
* Combines and returns queued messages combined into a single string.
* Combines as many messages as possible, while staying under MAX_PAYLOAD_SIZE.
*
* Combines as many messages as possible, without exceeding
* COMBINED_RESPONSE_CUTOFF in case of multiple response messages.
*
* Returns null if the queue is empty.
*/
public String popAndEncode(boolean fromOnlineEvent) {
@@ -140,7 +144,10 @@ public class NativeToJsMessageQueue {
int numMessagesToSend = 0;
for (JsMessage message : queue) {
int messageSize = calculatePackedMessageLength(message);
if (numMessagesToSend > 0 && totalPayloadLen + messageSize > MAX_PAYLOAD_SIZE && MAX_PAYLOAD_SIZE > 0) {
if (numMessagesToSend > 0 &&
COMBINED_RESPONSE_CUTOFF > 0 &&
totalPayloadLen + messageSize > COMBINED_RESPONSE_CUTOFF
) {
break;
}
totalPayloadLen += messageSize;
@@ -175,7 +182,10 @@ public class NativeToJsMessageQueue {
int numMessagesToSend = 0;
for (JsMessage message : queue) {
int messageSize = message.calculateEncodedLength() + 50; // overestimate.
if (numMessagesToSend > 0 && totalPayloadLen + messageSize > MAX_PAYLOAD_SIZE && MAX_PAYLOAD_SIZE > 0) {
if (numMessagesToSend > 0 &&
COMBINED_RESPONSE_CUTOFF > 0 &&
totalPayloadLen + messageSize > COMBINED_RESPONSE_CUTOFF
) {
break;
}
totalPayloadLen += messageSize;

View File

@@ -82,7 +82,7 @@ public class PluginResult {
this.messageType = binaryString ? MESSAGE_TYPE_BINARYSTRING : MESSAGE_TYPE_ARRAYBUFFER;
this.encodedMessage = Base64.encodeToString(data, Base64.NO_WRAP);
}
// The keepCallback and status of multipartMessages are ignored.
public PluginResult(Status status, List<PluginResult> multipartMessages) {
this.status = status.ordinal();

View File

@@ -31,18 +31,12 @@ class SystemCookieManager implements ICordovaCookieManager {
protected final WebView webView;
private final CookieManager cookieManager;
//Added because lint can't see the conditional RIGHT ABOVE this
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SystemCookieManager(WebView webview) {
webView = webview;
cookieManager = CookieManager.getInstance();
//REALLY? Nobody has seen this UNTIL NOW?
cookieManager.setAcceptFileSchemeCookies(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
cookieManager.setAcceptThirdPartyCookies(webView, true);
}
cookieManager.setAcceptThirdPartyCookies(webView, true);
}
public void setCookiesEnabled(boolean accept) {
@@ -59,16 +53,10 @@ class SystemCookieManager implements ICordovaCookieManager {
@SuppressWarnings("deprecation")
public void clearCookies() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
cookieManager.removeAllCookies(null);
} else {
cookieManager.removeAllCookie();
}
cookieManager.removeAllCookies(null);
}
public void flush() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
cookieManager.flush();
}
cookieManager.flush();
}
};

View File

@@ -150,14 +150,6 @@ public class SystemWebChromeClient extends WebChromeClient {
quotaUpdater.updateQuota(MAX_QUOTA);
}
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage)
{
if (consoleMessage.message() != null)
LOG.d(LOG_TAG, "%s: Line %d : %s" , consoleMessage.sourceId() , consoleMessage.lineNumber(), consoleMessage.message());
return super.onConsoleMessage(consoleMessage);
}
@Override
/**
* Instructs the client to show a prompt to ask the user to set the Geolocation permission state for the specified origin.
@@ -176,7 +168,6 @@ public class SystemWebChromeClient extends WebChromeClient {
{
geolocation.requestPermissions(0);
}
}
// API level 7 is required for this, see if we could lower this using something else
@@ -199,7 +190,6 @@ public class SystemWebChromeClient extends WebChromeClient {
* @return View The progress view.
*/
public View getVideoLoadingProgressView() {
if (mVideoProgressView == null) {
// Create a new Loading view programmatically.
@@ -218,36 +208,9 @@ public class SystemWebChromeClient extends WebChromeClient {
mVideoProgressView = layout;
}
return mVideoProgressView;
return mVideoProgressView;
}
// <input type=file> support:
// openFileChooser() is for pre KitKat and in KitKat mr1 (it's known broken in KitKat).
// For Lollipop, we use onShowFileChooser().
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
this.openFileChooser(uploadMsg, "*/*");
}
public void openFileChooser( ValueCallback<Uri> uploadMsg, String acceptType ) {
this.openFileChooser(uploadMsg, acceptType, null);
}
public void openFileChooser(final ValueCallback<Uri> uploadMsg, String acceptType, String capture)
{
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
parentEngine.cordova.startActivityForResult(new CordovaPlugin() {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData();
LOG.d(LOG_TAG, "Receive file chooser URL: " + result);
uploadMsg.onReceiveValue(result);
}
}, intent, FILECHOOSER_RESULTCODE);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean onShowFileChooser(WebView webView, final ValueCallback<Uri[]> filePathsCallback, final WebChromeClient.FileChooserParams fileChooserParams) {
// Check if multiple-select is specified
@@ -257,6 +220,13 @@ public class SystemWebChromeClient extends WebChromeClient {
}
Intent intent = fileChooserParams.createIntent();
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, selectMultiple);
// Uses Intent.EXTRA_MIME_TYPES to pass multiple mime types.
String[] acceptTypes = fileChooserParams.getAcceptTypes();
if (acceptTypes.length > 1) {
intent.setType("*/*"); // Accept all, filter mime types by Intent.EXTRA_MIME_TYPES.
intent.putExtra(Intent.EXTRA_MIME_TYPES, acceptTypes);
}
try {
parentEngine.cordova.startActivityForResult(new CordovaPlugin() {
@Override
@@ -288,7 +258,6 @@ public class SystemWebChromeClient extends WebChromeClient {
return true;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onPermissionRequest(final PermissionRequest request) {
LOG.d(LOG_TAG, "onPermissionRequest: " + Arrays.toString(request.getResources()));

View File

@@ -48,7 +48,7 @@ import java.util.Hashtable;
/**
* This class is the WebViewClient that implements callbacks for our web view.
* The kind of callbacks that happen here are regarding the rendering of the
* document instead of the chrome surrounding it, such as onPageStarted(),
* document instead of the chrome surrounding it, such as onPageStarted(),
* shouldOverrideUrlLoading(), etc. Related to but different than
* CordovaChromeClient.
*/
@@ -104,7 +104,7 @@ public class SystemWebViewClient extends WebViewClient {
// By default handle 401 like we'd normally do!
super.onReceivedHttpAuthRequest(view, handler, host, realm);
}
/**
* On received client cert request.
* The method forwards the request to any running plugins before using the default implementation.
@@ -113,7 +113,6 @@ public class SystemWebViewClient extends WebViewClient {
* @param request
*/
@Override
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void onReceivedClientCertRequest (WebView view, ClientCertRequest request)
{
@@ -334,7 +333,7 @@ public class SystemWebViewClient extends WebViewClient {
// Allow plugins to intercept WebView requests.
Uri remappedUri = resourceApi.remapUri(origUri);
if (!origUri.equals(remappedUri) || needsSpecialsInAssetUrlFix(origUri) || needsKitKatContentUrlFix(origUri)) {
if (!origUri.equals(remappedUri) || needsSpecialsInAssetUrlFix(origUri) || needsContentUrlFix(origUri)) {
CordovaResourceApi.OpenForReadResult result = resourceApi.openForRead(remappedUri, true);
return new WebResourceResponse(result.mimeType, "UTF-8", result.inputStream);
}
@@ -349,7 +348,7 @@ public class SystemWebViewClient extends WebViewClient {
}
}
private static boolean needsKitKatContentUrlFix(Uri uri) {
private static boolean needsContentUrlFix(Uri uri) {
return "content".equals(uri.getScheme());
}

View File

@@ -113,7 +113,7 @@ public class SystemWebViewEngine implements CordovaWebViewEngine {
//sometimes this can be called after calling webview.destroy() on destroy()
//thus resulting in a NullPointerException
if(webView!=null) {
webView.setNetworkAvailable(value);
webView.setNetworkAvailable(value);
}
}
@Override

3344
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,57 +1,47 @@
{
"name": "cordova-android",
"version": "8.1.0",
"version": "9.0.0",
"description": "cordova-android release",
"bin": {
"create": "bin/create"
},
"main": "bin/templates/cordova/Api.js",
"repository": {
"type": "git",
"url": "https://github.com/apache/cordova-android"
},
"bugs": {
"url": "https://github.com/apache/cordova-android/issues"
},
"repository": "github:apache/cordova-android",
"bugs": "https://github.com/apache/cordova-android/issues",
"keywords": [
"android",
"cordova",
"apache"
],
"scripts": {
"test": "npm run eslint && npm run cover && npm run java-unit-tests",
"test": "npm run lint && npm run cover && npm run java-unit-tests",
"unit-tests": "jasmine --config=spec/unit/jasmine.json",
"cover": "nyc jasmine --config=spec/coverage.json",
"e2e-tests": "jasmine --config=spec/e2e/jasmine.json",
"java-unit-tests": "node test/run_java_unit_tests.js",
"eslint": "eslint bin spec test"
"lint": "eslint . \"bin/**/!(*.*|gitignore)\"",
"clean-tests": "node bin/clean_test.js"
},
"author": "Apache Software Foundation",
"license": "Apache-2.0",
"dependencies": {
"android-versions": "^1.4.0",
"compare-func": "^1.3.2",
"cordova-common": "^3.2.0",
"nopt": "^4.0.1",
"android-versions": "^1.5.0",
"cordova-common": "^4.0.1",
"execa": "^4.0.2",
"fs-extra": "^9.0.1",
"nopt": "^4.0.3",
"properties-parser": "^0.3.1",
"q": "^1.5.1",
"shelljs": "^0.5.3"
"which": "^2.0.2"
},
"devDependencies": {
"eslint": "^5.12.0",
"eslint-config-semistandard": "^13.0.0",
"eslint-config-standard": "^12.0.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-node": "^8.0.1",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-standard": "^4.0.0",
"jasmine": "^3.3.1",
"jasmine-spec-reporter": "^4.2.1",
"nyc": "^14.1.1",
"rewire": "^4.0.1"
"@cordova/eslint-config": "^3.0.0",
"jasmine": "^3.5.0",
"jasmine-spec-reporter": "^5.0.2",
"nyc": "^15.1.0",
"rewire": "^5.0.0"
},
"engines": {
"node": ">=6.0.0"
"node": ">=10.0.0"
},
"engineStrict": true,
"nyc": {

View File

@@ -1,4 +0,0 @@
env:
jasmine: true
rules:
prefer-promise-reject-errors: off

View File

@@ -18,7 +18,7 @@
*/
var PluginInfoProvider = require('cordova-common').PluginInfoProvider;
var shell = require('shelljs');
const fs = require('fs-extra');
var cp = require('child_process');
var path = require('path');
var util = require('util');
@@ -77,7 +77,6 @@ module.exports.updateProject = function (projectid, platformpath, callback) {
}
callback(error);
});
};
/**
@@ -104,7 +103,7 @@ module.exports.buildProject = function (projectid, callback) {
*/
module.exports.removeProject = function (projectid) {
var projectDirName = getDirName(projectid);
shell.rm('-rf', projectDirName);
fs.removeSync(projectDirName);
};
/**

View File

@@ -17,28 +17,37 @@
under the License.
*/
var path = require('path');
var actions = require('./helpers/projectActions.js');
const os = require('os');
const fs = require('fs-extra');
const path = require('path');
const execa = require('execa');
const { PluginInfoProvider } = require('cordova-common');
var PLUGIN_ADD_TIMEOUT = 90000;
const createBin = path.join(__dirname, '../../bin/create');
const fakePluginPath = path.join(__dirname, 'fixtures/cordova-plugin-fake');
describe('plugin add', function () {
let tmpDir;
beforeEach(() => {
const tmpDirTemplate = path.join(os.tmpdir(), 'cordova-android-test-');
tmpDir = fs.realpathSync(fs.mkdtempSync(tmpDirTemplate));
});
afterEach(() => {
fs.removeSync(tmpDir);
});
it('Test#001 : create project and add a plugin with framework', function (done) {
var projectname = 'testpluginframework';
var projectid = 'com.test.plugin.framework';
var fakePluginPath = path.join(__dirname, 'fixtures/cordova-plugin-fake');
it('Test#001 : create project and add a plugin with framework', function () {
const projectname = 'testpluginframework';
const projectid = 'com.test.plugin.framework';
actions.createProject(projectname, projectid, function () {
actions.addPlugin(projectid, fakePluginPath, function (error) {
actions.removeProject(projectid);
if (error) {
console.error(error.stack);
}
expect(error).toBe(null);
done();
const projectPath = path.join(tmpDir, projectname);
const pluginInfo = new PluginInfoProvider().get(fakePluginPath);
return Promise.resolve()
.then(() => execa(createBin, [projectPath, projectid, projectname]))
.then(() => {
const Api = require(path.join(projectPath, 'cordova/Api.js'));
return new Api('android', projectPath).addPlugin(pluginInfo);
});
});
}, PLUGIN_ADD_TIMEOUT);
}, 90000);
});

View File

@@ -1,13 +1,13 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
buildToolsVersion "28.0.3"
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.example.anis.myapplication"
minSdkVersion 21
targetSdkVersion 28
targetSdkVersion 29
versionCode 1
versionName "1.0"
}

View File

@@ -6,7 +6,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.0.0'
classpath 'com.android.tools.build:gradle:4.0.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

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