Compare commits

..

2 Commits
9.0.x ... 8.0.0

Author SHA1 Message Date
エリス
d5069b1030 Set VERSION to 8.0.0 (via coho) 2019-02-13 15:22:06 +09:00
エリス
f48a5fc02a Update JS snapshot to version 8.0.0 (via coho) 2019-02-13 15:22:05 +09:00
251 changed files with 3081 additions and 11540 deletions

View File

@@ -1,22 +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.
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,3 +1,2 @@
bin/templates/project/assets/www/cordova.js
test/android/app
test/androidx/app
test/app

View File

@@ -1,27 +1,10 @@
# 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'
root: true
extends: semistandard
rules:
indent:
- error
- 4
camelcase: off
padded-blocks: off
operator-linebreak: off
no-throw-literal: off

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?
-->

View File

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

View File

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

View File

@@ -3,6 +3,7 @@ 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 = 'https://reviews.apache.org'
REVIEWBOARD_URL = 'http://reviews.apache.org'

37
.travis.yml Normal file
View File

@@ -0,0 +1,37 @@
language: android
sudo: false
jdk:
- oraclejdk8
android:
components:
- build-tools-28.0.3
env:
global:
# Keep gradle from crapping all over the log
- TERM=dumb
matrix:
- nodejs_version=6
- nodejs_version=8
- nodejs_version=10
install:
# Install a sdkmanager version that supports the --licenses switch and
# accept any Android SDK licenses. The output redirection prevents us from
# hitting the travis log size limit of 4MB which would fail the build.
- yes | sdkmanager tools > /dev/null
- yes | sdkmanager --licenses > /dev/null
- nvm install $nodejs_version
- npm install
- npm install -g codecov
script:
- gradle --version
- node --version
- npm --version
- 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:
- Check for Github issues that corresponds to your contribution and link or create them if necessary.
- Have a Jira issue open that corresponds to your contribution.
- 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-2020 Apache Cordova
Copyright 2015 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 20092017 Kristopher Michael Kowal. All rights reserved.
Copyright 20092012 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,42 +226,89 @@ 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.
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:
Copyright (c) Isaac Z. Schlueter and Contributors
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of 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.
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.
================================================================================
bin/node_modules/which
================================================================================
The ISC License
Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
All rights reserved.
Copyright (c) Isaac Z. Schlueter and Contributors
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.
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,5 +1,15 @@
Apache Cordova
Copyright 2015-2020 The Apache Software Foundation
Copyright 2015 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
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).

View File

@@ -19,21 +19,23 @@
#
-->
# Cordova Android
[![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)
[![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 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.
# 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.
[Apache Cordova](https://cordova.apache.org) is a project of The Apache Software Foundation (ASF).
## Requires
- Java JDK 1.8
- Android SDK [http://developer.android.com](https://developer.android.com/)
- Java JDK 1.8 or greater
- Android SDK [http://developer.android.com](http://developer.android.com)
## Cordova Android Developer Tools
@@ -60,4 +62,6 @@ 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,104 +18,7 @@
# under the License.
#
-->
## 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)
* [GH-827](https://github.com/apache/cordova-android/pull/827) chore: bump dependencies for release 8.1.0
* [GH-651](https://github.com/apache/cordova-android/pull/651) feat: added multiple selection for filepicker
* [GH-672](https://github.com/apache/cordova-android/pull/672) chore: compress files in /res with tinypng.com
* [GH-815](https://github.com/apache/cordova-android/pull/815) fix: `clean` command
* [GH-750](https://github.com/apache/cordova-android/pull/750) Don't request focus explicitly if not needed
* [GH-800](https://github.com/apache/cordova-android/pull/800) [GH-799](https://github.com/apache/cordova-android/pull/799) (android) Stop webview from restarting when activity resizes
* [GH-764](https://github.com/apache/cordova-android/pull/764) feat: Build app bundles (.aab files)
* [GH-788](https://github.com/apache/cordova-android/pull/788) Simplify `apkSorter` using `compare-func` package
* [GH-787](https://github.com/apache/cordova-android/pull/787) Simplify and fix promise handling in specs
* [GH-784](https://github.com/apache/cordova-android/pull/784) Properly handle promise in create script
* [GH-783](https://github.com/apache/cordova-android/pull/783) Do not clobber process properties with test mocks
* [GH-782](https://github.com/apache/cordova-android/pull/782) Do not clobber `console.log` to spy on it
* [GH-724](https://github.com/apache/cordova-android/pull/724) Add Node.js 12 to CI Services
* [GH-777](https://github.com/apache/cordova-android/pull/777) ci(travis): set `dist: trusty` in `.travis.yml`
* [GH-779](https://github.com/apache/cordova-android/pull/779) Consistent order from `ProjectBuilder.apkSorter`
* [GH-778](https://github.com/apache/cordova-android/pull/778) test: use verbose spec reporter
* [GH-774](https://github.com/apache/cordova-android/pull/774) `rewire` workaround for NodeJS 12
* [GH-772](https://github.com/apache/cordova-android/pull/772) `nyc@14` update in devDependencies
* [GH-765](https://github.com/apache/cordova-android/pull/765) ci(travis): Fix **Android** SDK
* [GH-713](https://github.com/apache/cordova-android/pull/713) Do not explicitly require modules from project directory
* [GH-676](https://github.com/apache/cordova-android/pull/676) Added allprojects repositories for Framework Release Builds
* [GH-699](https://github.com/apache/cordova-android/pull/699) Improve Gradle Build Arguments
* [GH-710](https://github.com/apache/cordova-android/pull/710) Fix deprecation warning in `SystemCookieManager`
* [GH-691](https://github.com/apache/cordova-android/pull/691) [GH-690](https://github.com/apache/cordova-android/pull/690): Run `prepare` with the correct `ConfigParser`
* [GH-673](https://github.com/apache/cordova-android/pull/673) Updated `Android_HOME` Test to Follow [GH-656](https://github.com/apache/cordova-android/pull/656) Change
## Release Notes for Cordova (Android) ##
### 8.0.0 (Feb 13, 2019)
* [GH-669](https://github.com/apache/cordova-android/pull/669) Added Missing License Headers

View File

@@ -1 +1 @@
9.0.0
8.0.0

36
appveyor.yml Normal file
View File

@@ -0,0 +1,36 @@
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
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().catch(err => {
android_sdk.print_newest_available_sdk_target().done(null, function (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().then(
check_reqs.run().done(
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

View File

@@ -1,28 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
const fs = require('fs');
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,7 +55,4 @@ var options = {
require('./templates/cordova/loggingHelper').adjustLoggerLevel(argv);
Api.createPlatform(argv.argv.remain[0], config, options).catch(err => {
console.error(err);
process.exitCode = 1;
});
Api.createPlatform(argv.argv.remain[0], config, options).done();

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,3 +1,5 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -17,9 +19,10 @@
under the License.
*/
var shell = require('shelljs');
var Q = require('q');
var path = require('path');
var fs = require('fs-extra');
var utils = require('../../bin/lib/utils');
var fs = require('fs');
var check_reqs = require('./../templates/cordova/lib/check_reqs');
var ROOT = path.join(__dirname, '..', '..');
@@ -31,12 +34,20 @@ 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');
}
@@ -45,33 +56,53 @@ 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;
}
fs.copySync(srcCordovaJsPath, path.join(app_path, 'assets', 'www', 'cordova.js'));
shell.cp('-f', 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
fs.ensureDirSync(platform_www);
fs.copySync(srcCordovaJsPath, path.join(platform_www, 'cordova.js'));
shell.mkdir('-p', path.join(projectPath, 'platform_www'));
shell.cp('-f', srcCordovaJsPath, path.join(projectPath, 'platform_www'));
// Copy cordova-js-src directory into platform_www directory.
// We need these files to build cordova.js if using browserify method.
fs.copySync(path.join(ROOT, 'cordova-js-src'), path.join(platform_www, 'cordova-js-src'));
shell.cp('-rf', path.join(ROOT, 'cordova-js-src'), path.join(projectPath, 'platform_www'));
// 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 {
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'));
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);
}
}
@@ -111,8 +142,8 @@ function writeProjectProperties (projectPath, target_api) {
// This makes no sense, what if you're building with a different build system?
function prepBuildFiles (projectPath) {
var buildModule = require('../templates/cordova/lib/builders/builders');
buildModule.getBuilder(projectPath).prepBuildFiles();
var buildModule = require(path.resolve(projectPath, 'cordova/lib/builders/builders'));
buildModule.getBuilder().prepBuildFiles();
}
function copyBuildRules (projectPath, isLegacy) {
@@ -120,12 +151,12 @@ function copyBuildRules (projectPath, isLegacy) {
if (isLegacy) {
// The project's build.gradle is identical to the earlier build.gradle, so it should still work
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'));
shell.cp('-f', path.join(srcDir, 'legacy', 'build.gradle'), projectPath);
shell.cp('-f', path.join(srcDir, 'wrapper.gradle'), projectPath);
} else {
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'));
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);
}
}
@@ -134,29 +165,24 @@ 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.
fs.removeSync(destScriptsDir);
shell.rm('-rf', destScriptsDir);
// Copy in the new ones.
fs.copySync(srcScriptsDir, destScriptsDir);
shell.cp('-r', srcScriptsDir, projectPath);
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'));
let nodeModulesDir = path.join(ROOT, 'node_modules');
if (fs.existsSync(nodeModulesDir)) shell.cp('-r', nodeModulesDir, destScriptsDir);
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 is updating the
// platform and project-level commands. the below `sed` 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.
const templatesCordovaRegex = /templates\/cordova\//;
utils.replaceFileContents(android_sdk_version, templatesCordovaRegex, '');
utils.replaceFileContents(check_reqs, templatesCordovaRegex, '');
shell.sed('-i', /templates\/cordova\//, '', android_sdk_version);
shell.sed('-i', /templates\/cordova\//, '', check_reqs);
}
/**
@@ -171,19 +197,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 Promise.reject(new CordovaError(msg + 'Must look like: `com.company.Name`. Currently is: `' + package_name + '`'));
return Q.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 Promise.reject(new CordovaError(msg + '"class" is a reserved word'));
return Q.reject(new CordovaError(msg + '"class" is a reserved word'));
}
return Promise.resolve();
return Q.resolve();
}
/**
* Test whether given string is acceptable for use as a project name
* Test whether a project name is acceptable for use as an android class.
* Returns a promise, fulfilled if the project name is acceptable; rejected
* otherwise.
*/
@@ -191,10 +217,20 @@ function validateProjectName (project_name) {
var msg = 'Error validating project name. ';
// Make sure there's something there
if (project_name === '') {
return Promise.reject(new CordovaError(msg + 'Project name cannot be empty'));
return Q.reject(new CordovaError(msg + 'Project name cannot be empty'));
}
return Promise.resolve();
// 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();
}
/**
@@ -216,18 +252,19 @@ 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 Promise.reject(new CordovaError('Project already exists! Delete and recreate'));
return Q.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();
@@ -235,7 +272,7 @@ exports.create = function (project_path, config, options, events) {
// Make the package conform to Java package types
return exports.validatePackageName(package_name)
.then(function () {
return exports.validateProjectName(project_name);
exports.validateProjectName(project_name);
}).then(function () {
// Log the given values for the project
events.emit('log', 'Creating Cordova project for the Android platform:');
@@ -247,55 +284,57 @@ exports.create = function (project_path, config, options, events) {
events.emit('verbose', 'Copying android template project to ' + project_path);
var project_template_dir = options.customTemplate || path.join(ROOT, 'bin', 'templates', 'project');
var app_path = path.join(project_path, 'app', 'src', 'main');
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');
// 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'));
// 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'));
// Manually create directories that would be empty within the template (since git doesn't track directories).
fs.ensureDirSync(path.join(app_path, 'libs'));
// Manually create directories that would be empty within the template (since git doesn't track directories).
shell.mkdir(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');
fs.ensureDirSync(java_path);
fs.ensureDirSync(assets_path);
fs.ensureDirSync(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');
shell.mkdir('-p', java_path);
shell.mkdir('-p', assets_path);
shell.mkdir('-p', 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');
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);
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);
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)
.setTargetSdkVersion(target_api.split('-')[1])
.getActivity().setName(safe_activity_name);
var manifest_path = path.join(app_path, 'AndroidManifest.xml');
manifest.write(manifest_path);
exports.copyScripts(project_path);
exports.copyBuildRules(project_path);
var manifest_path = path.join(app_path, 'AndroidManifest.xml');
manifest.write(manifest_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));
}).then(() => project_path);
}).thenResolve(project_path);
};
function generateDoneMessage (type, link) {
@@ -309,6 +348,7 @@ 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' +
@@ -319,5 +359,5 @@ exports.update = function (projectPath, options, events) {
'\tcordova platform add android\n'
;
return Promise.reject(errorString);
return Q.reject(errorString);
};

View File

@@ -1,47 +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.
*/
/*
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,24 +17,14 @@
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;
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';
@@ -61,324 +51,320 @@ function setupEvents (externalEventEmitter) {
* The PlatformApi instance also should define the following field:
*
* * platform: String that defines a platform name.
* @class Api
*/
class Api {
constructor (platform, platformRootDir, events) {
this.platform = PLATFORM;
this.root = path.resolve(__dirname, '..');
function Api (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')
};
}
/**
* 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;
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.
*/
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
.then(() => true);
}
/**
* 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);
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;
}
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'),
// NOTE: Due to platformApi spec we need to return relative paths here
cordovaJs: 'bin/templates/project/assets/www/cordova.js',
cordovaJsSrc: 'cordova-js-src'
};
}
/**
* 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) {
var PlatformApi = require(path.resolve(destination, 'cordova/Api'));
return new PlatformApi(PLATFORM, destination, events);
});
} catch (e) {
events.emit('error', 'createPlatform is not callable from the android project API.');
throw (e);
}
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) {
var PlatformApi = require(path.resolve(destination, 'cordova/Api'));
return new PlatformApi('android', destination, events);
});
} catch (e) {
events.emit('error', 'updatePlatform is not callable from the android project API, you will need to do this manually.');
throw (e);
}
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) {
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();
}
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);
};
/**
* 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 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
.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.apkPaths.map(function (apkPath) {
return {
buildType: buildResults.buildType,
buildMethod: buildResults.buildMethod,
path: apkPath,
type: 'apk'
};
});
});
};
/**
* 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 = {};
}
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.
*/
Api.prototype.requirements = function () {
return require('./lib/check_reqs').check_all();
};
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,9 +17,10 @@
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 = {};
@@ -43,7 +44,7 @@ function isEmulator (line) {
* devices/emulators
*/
Adb.devices = function (opts) {
return execa('adb', ['devices'], { cwd: os.tmpdir() }).then(({ stdout: output }) => {
return spawn('adb', ['devices'], { cwd: os.tmpdir() }).then(function (output) {
return output.split('\n').filter(function (line) {
// Filter out either real devices or emulators, depending on options
return (line && opts && opts.emulators) ? isEmulator(line) : isDevice(line);
@@ -57,7 +58,7 @@ Adb.install = function (target, packagePath, opts) {
events.emit('verbose', 'Installing apk ' + packagePath + ' on target ' + target + '...');
var args = ['-s', target, 'install'];
if (opts && opts.replace) args.push('-r');
return execa('adb', args.concat(packagePath), { cwd: os.tmpdir() }).then(({ stdout: output }) => {
return spawn('adb', args.concat(packagePath), { cwd: os.tmpdir() }).then(function (output) {
// 'adb install' seems to always returns no error, even if installation fails
// so we catching output to detect installation failure
if (output.match(/Failure/)) {
@@ -69,30 +70,31 @@ Adb.install = function (target, packagePath, opts) {
'\nEither uninstall an app or increment the versionCode.';
}
return Promise.reject(new CordovaError('Failed to install apk to device: ' + output));
return Q.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 execa('adb', ['-s', target, 'uninstall', packageId], { cwd: os.tmpdir() }).then(({ stdout }) => stdout);
return spawn('adb', ['-s', target, 'uninstall', packageId], { cwd: os.tmpdir() });
};
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 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}`)));
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));
});
};
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((error) => {
return Promise.reject(new CordovaError('Failed to start application "' +
activityName + '"" on device: ' + error));
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));
});
};

View File

@@ -18,111 +18,139 @@
*/
var fs = require('fs');
var et = require('elementtree');
var xml = require('cordova-common').xmlHelpers;
var DEFAULT_ORIENTATION = 'default';
/** Wraps an AndroidManifest file */
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');
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")');
}
}
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;
}
};
};
['minSdkVersion', 'maxSdkVersion', 'targetSdkVersion'].forEach(function (sdkPrefName) {
// Copy variable reference to avoid closure issues
var prefName = sdkPrefName;
AndroidManifest.prototype['get' + capitalize(prefName)] = function () {
var usesSdk = this.doc.getroot().find('./uses-sdk');
return usesSdk && usesSdk.attrib['android:' + prefName];
};
AndroidManifest.prototype['set' + capitalize(prefName)] = function (prefValue) {
var usesSdk = this.doc.getroot().find('./uses-sdk');
if (!usesSdk && prefValue) { // if there is no required uses-sdk element, we should create it first
usesSdk = new et.Element('uses-sdk');
this.doc.getroot().append(usesSdk);
}
if (prefValue) {
usesSdk.attrib['android:' + prefName] = prefValue;
}
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;
function capitalize (str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}

View File

@@ -55,148 +55,148 @@ function getRelativeLibraryPath (parentDir, subDir) {
return (path.sep === '\\') ? libraryPath.replace(/\\/g, '/') : libraryPath;
}
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 = {};
}
}
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');
}
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

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

View File

@@ -17,7 +17,7 @@
under the License.
*/
const execa = require('execa');
var superspawn = require('cordova-common').superspawn;
var suffix_number_regex = /(\d+)$/;
// Used for sorting Android targets, example strings to sort:
@@ -29,14 +29,17 @@ 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) {
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);
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;
}
}
module.exports.print_newest_available_sdk_target = function () {
@@ -46,8 +49,6 @@ 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,
@@ -63,7 +64,6 @@ 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 execa('android', ['list', 'target']).then(result => parse_targets(result.stdout));
return superspawn.spawn('android', ['list', 'target']).then(parse_targets);
};
module.exports.list_targets_with_avdmanager = function () {
return execa('avdmanager', ['list', 'target']).then(result => parse_targets(result.stdout));
return superspawn.spawn('avdmanager', ['list', 'target']).then(parse_targets);
};
module.exports.list_targets = function () {

View File

@@ -1,3 +1,5 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -17,6 +19,7 @@
under the License.
*/
var Q = require('q');
var path = require('path');
var fs = require('fs');
var nopt = require('nopt');
@@ -25,9 +28,8 @@ var Adb = require('./Adb');
var builders = require('./builders/builders');
var events = require('cordova-common').events;
const execa = require('execa');
var spawn = require('cordova-common').superspawn.spawn;
var CordovaError = require('cordova-common').CordovaError;
var PackageType = require('./PackageType');
module.exports.parseBuildOptions = parseOpts;
function parseOpts (options, resolvedTarget, projectRoot) {
@@ -36,15 +38,12 @@ function parseOpts (options, resolvedTarget, projectRoot) {
prepenv: Boolean,
versionCode: String,
minSdkVersion: String,
maxSdkVersion: String,
targetSdkVersion: String,
gradleArg: [String, Array],
keystore: path,
alias: String,
storePassword: String,
password: String,
keystoreType: String,
packageType: String
keystoreType: String
}, {}, options.argv, 0);
// Android Studio Build method is the default
@@ -57,8 +56,6 @@ function parseOpts (options, resolvedTarget, projectRoot) {
if (options.argv.versionCode) { ret.extraArgs.push('-PcdvVersionCode=' + options.argv.versionCode); }
if (options.argv.minSdkVersion) { ret.extraArgs.push('-PcdvMinSdkVersion=' + options.argv.minSdkVersion); }
if (options.argv.maxSdkVersion) { ret.extraArgs.push('-PcdvMaxSdkVersion=' + options.argv.maxSdkVersion); }
if (options.argv.targetSdkVersion) { ret.extraArgs.push('-PcdvTargetSdkVersion=' + options.argv.targetSdkVersion); }
if (options.argv.gradleArg) {
ret.extraArgs = ret.extraArgs.concat(options.argv.gradleArg);
}
@@ -67,14 +64,14 @@ function parseOpts (options, resolvedTarget, projectRoot) {
if (options.argv.keystore) { packageArgs.keystore = path.relative(projectRoot, path.resolve(options.argv.keystore)); }
['alias', 'storePassword', 'password', 'keystoreType', 'packageType'].forEach(function (flagName) {
['alias', 'storePassword', 'password', 'keystoreType'].forEach(function (flagName) {
if (options.argv[flagName]) { packageArgs[flagName] = options.argv[flagName]; }
});
var buildConfig = options.buildConfig;
// If some values are not specified as command line arguments - use build config to supplement them.
// Command line arguments have precedence over build config.
// Command line arguemnts have precedence over build config.
if (buildConfig) {
if (!fs.existsSync(buildConfig)) {
throw new Error('Specified build config file does not exist: ' + buildConfig);
@@ -92,7 +89,7 @@ function parseOpts (options, resolvedTarget, projectRoot) {
events.emit('log', 'Reading the keystore from: ' + packageArgs.keystore);
}
['alias', 'storePassword', 'password', 'keystoreType', 'packageType'].forEach(function (key) {
['alias', 'storePassword', 'password', 'keystoreType'].forEach(function (key) {
packageArgs[key] = packageArgs[key] || androidInfo[key];
});
}
@@ -104,38 +101,11 @@ function parseOpts (options, resolvedTarget, projectRoot) {
}
if (!ret.packageInfo) {
// The following loop is to decide whether to print a warning about generating a signed archive
// We only want to produce a warning if they are using a config property that is related to signing, but
// missing the required properties for signing. We don't want to produce a warning if they are simply
// using a build property that isn't related to signing, such as --packageType
let shouldWarn = false;
const signingKeys = ['keystore', 'alias', 'storePassword', 'password', 'keystoreType'];
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
shouldWarn = true;
}
}
if (shouldWarn) {
if (Object.keys(packageArgs).length > 0) {
events.emit('warn', '\'keystore\' and \'alias\' need to be specified to generate a signed archive.');
}
}
if (packageArgs.packageType) {
const VALID_PACKAGE_TYPES = [PackageType.APK, PackageType.BUNDLE];
if (VALID_PACKAGE_TYPES.indexOf(packageArgs.packageType) === -1) {
events.emit('warn', '"' + packageArgs.packageType + '" is an invalid packageType. Valid values are: ' + VALID_PACKAGE_TYPES.join(', ') + '\nDefaulting packageType to ' + PackageType.APK);
ret.packageType = PackageType.APK;
} else {
ret.packageType = packageArgs.packageType;
}
} else {
ret.packageType = PackageType.APK;
}
return ret;
}
@@ -174,17 +144,10 @@ module.exports.run = function (options, optResolvedTarget) {
return;
}
return builder.build(opts).then(function () {
var paths;
if (opts.packageType === PackageType.BUNDLE) {
paths = builder.findOutputBundles(opts.buildType);
events.emit('log', 'Built the following bundle(s): \n\t' + paths.join('\n\t'));
} else {
paths = builder.findOutputApks(opts.buildType, opts.arch);
events.emit('log', 'Built the following apk(s): \n\t' + paths.join('\n\t'));
}
var apkPaths = builder.findOutputApks(opts.buildType, opts.arch);
events.emit('log', 'Built the following apk(s): \n\t' + apkPaths.join('\n\t'));
return {
paths: paths,
apkPaths: apkPaths,
buildType: opts.buildType
};
});
@@ -201,37 +164,27 @@ 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 Promise.race([
helper(),
timeout(5000, new CordovaError(
'Device communication timed out. Try unplugging & replugging the device.'
))
]).catch(err => {
return helper().timeout(1000, new CordovaError('Device communication timed out. Try unplugging & replugging the device.')).then(null, function (err) {
if (/timed out/.exec('' + err)) {
// adb kill-server doesn't seem to do the trick.
// Could probably find a x-platform version of killall, but I'm not actually
// sure that this scenario even happens on non-OSX machines.
events.emit('verbose', 'adb timed out while detecting device/emulator architecture. Killing adb and trying again.');
return execa('killall', ['adb']).then(function () {
return spawn('killall', ['adb']).then(function () {
return helper().then(null, function () {
// The double kill is sadly often necessary, at least on mac.
events.emit('warn', 'adb timed out a second time while detecting device/emulator architecture. Killing adb and trying again.');
return execa('killall', ['adb']).then(function () {
return spawn('killall', ['adb']).then(function () {
return helper().then(null, function () {
return Promise.reject(new CordovaError('adb timed out a third time while detecting device/emulator architecture. Try unplugging & replugging the device.'));
return Q.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 Promise.reject(err);
return Q.reject(err);
});
}
throw err;
@@ -262,23 +215,45 @@ module.exports.findBestApkForArchitecture = function (buildResults, arch) {
};
function PackageInfo (keystore, alias, storePassword, password, 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));
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
};
}
}
PackageInfo.prototype = {
appendToProperties: function (propertiesParser) {
for (const { name, value } of this.data) propertiesParser.set(name, value);
propertiesParser.save();
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;
}
};
@@ -290,11 +265,8 @@ module.exports.help = function () {
console.log(' \'--nobuild\': will skip build process (useful when using run command)');
console.log(' \'--prepenv\': don\'t build, but copy in build scripts where necessary');
console.log(' \'--versionCode=#\': Override versionCode for this build. Useful for uploading multiple APKs.');
console.log(' \'--minSdkVersion=#\': Override minSdkVersion for this build.');
console.log(' \'--maxSdkVersion=#\': Override maxSdkVersion for this build. (Not Recommended)');
console.log(' \'--targetSdkVersion=#\': Override targetSdkVersion for this build.');
console.log(' \'--minSdkVersion=#\': Override minSdkVersion for this build. Useful for uploading multiple APKs.');
console.log(' \'--gradleArg=<gradle command line arg>\': Extra args to pass to the gradle command. Use one flag per arg. Ex. --gradleArg=-PcdvBuildMultipleApks=true');
console.log(' \'--packageType=<apk|bundle>\': Builds an APK or a bundle');
console.log('');
console.log('Signed APK flags (overwrites debug/release-signing.proprties) :');
console.log(' \'--keystore=<path to keystore>\': Key store used to build a signed archive. (Required)');

View File

@@ -16,15 +16,17 @@
specific language governing permissions and limitations
under the License.
*/
/* eslint no-self-assign: 0 */
/* eslint no-unused-vars: 0 */
var fs = require('fs-extra');
var Q = require('q');
var fs = require('fs');
var path = require('path');
const execa = require('execa');
var shell = require('shelljs');
var spawn = require('cordova-common').superspawn.spawn;
var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;
var check_reqs = require('../check_reqs');
var PackageType = require('../PackageType');
const { createEditor } = require('properties-parser');
const MARKER = 'YOUR CHANGES WILL BE ERASED!';
const SIGNING_PROPERTIES = '-signing.properties';
@@ -32,115 +34,23 @@ 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, '../../..');
this.apkDir = path.join(this.root, 'app', 'build', 'outputs', 'apk');
this.aabDir = path.join(this.root, 'app', 'build', 'outputs', 'bundle');
this.binDir = path.join(this.root, 'app', 'build', 'outputs', 'apk');
}
getArgs (cmd, opts) {
let args;
let buildCmd = cmd;
if (opts.packageType === PackageType.BUNDLE) {
if (cmd === 'release') {
buildCmd = ':app:bundleRelease';
} else if (cmd === 'debug') {
buildCmd = ':app:bundleDebug';
}
if (cmd === 'release') {
cmd = 'cdvBuildRelease';
} else if (cmd === 'debug') {
cmd = 'cdvBuildDebug';
}
args = [buildCmd, '-b', path.join(this.root, 'build.gradle')];
} else {
if (cmd === 'release') {
buildCmd = 'cdvBuildRelease';
} else if (cmd === 'debug') {
buildCmd = 'cdvBuildDebug';
}
let args = [cmd, '-b', path.join(this.root, 'build.gradle')];
args = [buildCmd, '-b', path.join(this.root, 'build.gradle')];
if (opts.arch) {
args.push('-PcdvBuildArch=' + opts.arch);
}
if (opts.arch) {
args.push('-PcdvBuildArch=' + opts.arch);
}
args.push.apply(args, opts.extraArgs);
@@ -157,7 +67,7 @@ class ProjectBuilder {
if (fs.existsSync(gradlePath)) {
// Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
} else {
return execa(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], { stdio: 'inherit' });
return spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], { stdio: 'inherit' });
}
}
@@ -209,7 +119,7 @@ class ProjectBuilder {
try {
fs.accessSync(subProjectGradle, fs.F_OK);
} catch (e) {
fs.copySync(pluginBuildGradle, subProjectGradle);
shell.cp('-f', pluginBuildGradle, subProjectGradle);
}
};
@@ -299,25 +209,22 @@ class ProjectBuilder {
return self.runGradleWrapper(gradlePath);
}).then(function () {
return self.prepBuildFiles();
}).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();
}).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);
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);
var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
var propertiesFilePath = path.join(self.root, propertiesFile);
if (opts.packageInfo) {
fs.ensureFileSync(signingPropertiesPath);
const signingProperties = createEditor(signingPropertiesPath);
signingProperties.addHeadComment(TEMPLATE);
opts.packageInfo.appendToProperties(signingProperties);
fs.writeFileSync(propertiesFilePath, TEMPLATE + opts.packageInfo.toProperties());
} else if (isAutoGenerated(propertiesFilePath)) {
shell.rm('-f', propertiesFilePath);
}
});
}
@@ -330,43 +237,57 @@ class ProjectBuilder {
var wrapper = path.join(this.root, 'gradlew');
var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
return execa(wrapper, args, { stdio: 'inherit' })
.catch(function (error) {
return spawn(wrapper, args, { stdio: 'pipe' })
.progress(function (stdio) {
if (stdio.stderr) {
/*
* Workaround for the issue with Java printing some unwanted information to
* stderr instead of stdout.
* This function suppresses 'Picked up _JAVA_OPTIONS' message from being
* printed to stderr. See https://issues.apache.org/jira/browse/CB-9971 for
* explanation.
*/
var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(stdio.stderr.toString());
if (suppressThisLine) {
return;
}
process.stderr.write(stdio.stderr);
} else {
process.stdout.write(stdio.stdout);
}
}).catch(function (error) {
if (error.toString().indexOf('failed to find target with hash string') >= 0) {
return check_reqs.check_android_target(error).then(function () {
// If due to some odd reason - check_android_target succeeds
// we should still fail here.
throw error;
return Q.reject(error);
});
}
throw error;
return Q.reject(error);
});
}
clean (opts) {
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'));
var builder = this;
var wrapper = path.join(this.root, 'gradlew');
var args = builder.getArgs('clean', opts);
return Q().then(function () {
return spawn(wrapper, args, { stdio: 'inherit' });
})
.then(function () {
shell.rm('-rf', path.join(builder.root, 'out'));
['debug', 'release'].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);
});
['debug', 'release'].forEach(function (config) {
var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES);
if (isAutoGenerated(propertiesFilePath)) {
shell.rm('-f', propertiesFilePath);
}
});
});
}
findOutputApks (build_type, arch) {
return findOutputFilesHelper(this.apkDir, build_type, arch, 'apk').sort(fileSorter);
}
findOutputBundles (build_type) {
return findOutputFilesHelper(this.aabDir, build_type, false, 'aab').sort(fileSorter);
return findOutputApksHelper(this.binDir, build_type, arch).sort(apkSorter);
}
fetchBuildResults (build_type, arch) {
@@ -378,3 +299,72 @@ class ProjectBuilder {
}
module.exports = ProjectBuilder;
function apkSorter (fileA, fileB) {
// De-prioritize arch specific builds
var archSpecificRE = /-x86|-arm/;
if (archSpecificRE.exec(fileA)) {
return 1;
} else if (archSpecificRE.exec(fileB)) {
return -1;
}
// De-prioritize unsigned builds
var unsignedRE = /-unsigned/;
if (unsignedRE.exec(fileA)) {
return 1;
} else if (unsignedRE.exec(fileB)) {
return -1;
}
var timeDiff = fs.statSync(fileB).mtime - fs.statSync(fileA).mtime;
return timeDiff === 0 ? fileA.length - fileB.length : timeDiff;
}
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;
}
function isAutoGenerated (file) {
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
}

View File

@@ -24,10 +24,10 @@ const CordovaError = require('cordova-common').CordovaError;
*
* @return {Builder} A builder instance for specified build type.
*/
module.exports.getBuilder = function (projectPath) {
module.exports.getBuilder = function () {
try {
const Builder = require('./ProjectBuilder');
return new Builder(projectPath);
return new Builder();
} catch (err) {
throw new CordovaError('Failed to instantiate ProjectBuilder builder: ' + err);
}

View File

@@ -1,3 +1,5 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -17,37 +19,24 @@
under the License.
*/
const execa = require('execa');
var shelljs = require('shelljs');
var child_process = require('child_process');
var Q = require('q');
var path = require('path');
var fs = require('fs-extra');
var fs = require('fs');
var os = require('os');
var which = require('which');
var REPO_ROOT = path.join(__dirname, '..', '..', '..', '..');
var PROJECT_ROOT = path.join(__dirname, '..', '..');
const { CordovaError, ConfigParser, events } = require('cordova-common');
var CordovaError = require('cordova-common').CordovaError;
var superspawn = require('cordova-common').superspawn;
var android_sdk = require('./android_sdk');
const { createEditor } = require('properties-parser');
function forgivingWhichSync (cmd) {
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);
}
}
try {
return fs.realpathSync(shelljs.which(cmd));
} catch (e) {
return '';
}
return null;
}
module.exports.isWindows = function () {
@@ -58,49 +47,31 @@ module.exports.isDarwin = function () {
return (os.platform() === 'darwin');
};
/**
* @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}"
*/
// Get valid target from framework/project.properties if run from this repo
// Otherwise get target from project.properties file within a generated cordova-android project
module.exports.get_target = function () {
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}.`);
function extractFromFile (filePath) {
var target = shelljs.grep(/\btarget=/, filePath);
if (!target) {
throw new Error('Could not find android target within: ' + filePath);
}
return target.split('=')[1].trim();
}
return target;
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);
};
// Returns a promise. Called only by build and clean commands.
module.exports.check_ant = function () {
return execa('ant', ['-version']).then(({ stdout: output }) => {
return superspawn.spawn('ant', ['-version']).then(function (output) {
// Parse Ant version from command output
return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1];
}).catch(function (err) {
@@ -117,18 +88,19 @@ 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 = execa.sync(path.join(__dirname, 'getASPath.bat'));
var result = child_process.spawnSync(path.join(__dirname, 'getASPath.bat'));
// console.log('result.stdout =' + result.stdout.toString());
// console.log('result.stderr =' + result.stderr.toString());
if (result.stderr.toString().length > 0) {
var androidPath = path.join(process.env.ProgramFiles, 'Android') + '/';
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; }
}
}
@@ -153,30 +125,31 @@ module.exports.get_gradle_wrapper = function () {
// Returns a promise. Called only by build and clean commands.
module.exports.check_gradle = function () {
var sdkDir = process.env.ANDROID_SDK_ROOT || process.env.ANDROID_HOME;
var sdkDir = process.env['ANDROID_HOME'];
var d = Q.defer();
if (!sdkDir) {
return Promise.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Could not find Android SDK directory.\n' +
'Might need to install Android SDK or set up \'ANDROID_SDK_ROOT\' env variable.'));
return Q.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Could not find Android SDK directory.\n' +
'Might need to install Android SDK or set up \'ANDROID_HOME\' env variable.'));
}
var gradlePath = module.exports.get_gradle_wrapper();
if (gradlePath.length !== 0) 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'));
if (gradlePath.length !== 0) { d.resolve(gradlePath); } else {
d.reject(new CordovaError('Could not find an installed version of Gradle either in Android Studio,\n' +
'or on your system to install the gradle wrapper. Please include gradle \n' +
'in your path, or install Android Studio'));
}
return d.promise;
};
// Returns a promise.
module.exports.check_java = function () {
var javacPath = forgivingWhichSync('javac');
var hasJavaHome = !!process.env.JAVA_HOME;
return Promise.resolve().then(function () {
var hasJavaHome = !!process.env['JAVA_HOME'];
return Q().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) {
@@ -184,8 +157,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 execa(find_java).then(({ stdout }) => {
process.env.JAVA_HOME = stdout;
return superspawn.spawn(find_java).then(function (stdout) {
process.env['JAVA_HOME'] = stdout.trim();
}).catch(function (err) {
if (err) {
throw new CordovaError(default_java_error_msg);
@@ -193,36 +166,39 @@ 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()) {
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);
// 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;
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 execa('javac', ['-version'], { all: true })
.then(({ all: output }) => {
return Q.denodeify(child_process.exec)('javac -version')
.then(outputs => {
// outputs contains two entries: stdout and stderr
// 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];
}, () => {
@@ -230,9 +206,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);
});
@@ -241,74 +217,55 @@ module.exports.check_java = function () {
// Returns a promise.
module.exports.check_android = function () {
return Promise.resolve().then(function () {
function maybeSetAndroidHome (value) {
if (!hasAndroidHome && fs.existsSync(value)) {
hasAndroidHome = true;
process.env.ANDROID_SDK_ROOT = value;
}
}
return Q().then(function () {
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));
var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
function maybeSetAndroidHome (value) {
if (!hasAndroidHome && fs.existsSync(value)) {
hasAndroidHome = true;
process.env['ANDROID_HOME'] = value;
}
}
// 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_HOME) {
// Fallback to deprecated `ANDROID_HOME` variable
maybeSetAndroidHome(path.join(process.env.ANDROID_HOME));
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 (module.exports.isWindows()) {
// Android Studio 1.0 installer
if (process.env.LOCALAPPDATA) {
maybeSetAndroidHome(path.join(process.env.LOCALAPPDATA, 'Android', 'sdk'));
}
if (process.env.ProgramFiles) {
maybeSetAndroidHome(path.join(process.env.ProgramFiles, 'Android', 'sdk'));
}
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'sdk'));
maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'sdk'));
// Android Studio pre-1.0 installer
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'));
}
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'android-studio', 'sdk'));
maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'android-studio', 'sdk'));
// Stand-alone installer
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'));
}
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'android-sdk'));
maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'android-sdk'));
} else if (module.exports.isDarwin()) {
// Android Studio 1.0 installer
if (process.env.HOME) {
maybeSetAndroidHome(path.join(process.env.HOME, 'Library', 'Android', 'sdk'));
}
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_SDK_ROOT, but we do have some tools on the PATH, try to infer from the tooling PATH.
// If we dont have ANDROID_HOME, 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);
@@ -316,7 +273,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_SDK_ROOT\' environment variable. Try setting it manually.\n' +
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
'Detected \'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.');
}
@@ -327,7 +284,7 @@ module.exports.check_android = function () {
if (path.basename(parentDir) === 'platform-tools') {
maybeSetAndroidHome(grandParentDir);
} else {
throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' environment variable. Try setting it manually.\n' +
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
'Detected \'adb\' command at ' + parentDir + ' but no \'platform-tools\' directory found near.\n' +
'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'platform-tools directory.');
}
@@ -338,29 +295,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_SDK_ROOT\' environment variable. Try setting it manually.\n' +
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
'Detected \'avdmanager\' command at ' + parentDir + ' but no \'tools' + path.sep + 'bin\' directory found near.\n' +
'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'tools' + path.sep + 'bin directory.');
}
}
}
if (!process.env.ANDROID_SDK_ROOT) {
throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' environment variable. Try setting it manually.\n' +
if (!process.env['ANDROID_HOME']) {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
'Failed to find \'android\' command in your \'PATH\'. Try update your \'PATH\' to include path to valid SDK directory.');
}
if (!fs.existsSync(process.env.ANDROID_SDK_ROOT)) {
throw new CordovaError('\'ANDROID_SDK_ROOT\' environment variable is set to non-existent path: ' + process.env.ANDROID_SDK_ROOT +
if (!fs.existsSync(process.env['ANDROID_HOME'])) {
throw new CordovaError('\'ANDROID_HOME\' environment variable is set to non-existent path: ' + process.env['ANDROID_HOME'] +
'\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_SDK_ROOT, 'tools');
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools');
}
if (hasAndroidHome && !adbInPath) {
process.env.PATH += path.delimiter + path.join(process.env.ANDROID_SDK_ROOT, 'platform-tools');
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'platform-tools');
}
if (hasAndroidHome && !avdmanagerInPath) {
process.env.PATH += path.delimiter + path.join(process.env.ANDROID_SDK_ROOT, 'tools', 'bin');
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools', 'bin');
}
return hasAndroidHome;
});
@@ -405,12 +362,10 @@ module.exports.check_android_target = function (originalError) {
// Returns a promise.
module.exports.run = function () {
console.log('Checking Java JDK and Android SDK versions');
console.log('ANDROID_SDK_ROOT=' + process.env.ANDROID_SDK_ROOT + ' (recommended setting)');
console.log('ANDROID_HOME=' + process.env.ANDROID_HOME + ' (DEPRECATED)');
return Promise.all([this.check_java(), this.check_android()]).then(function (values) {
console.log('Using Android SDK: ' + process.env.ANDROID_SDK_ROOT);
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)');
if (!String(values[0]).startsWith('1.8.')) {
throw new CordovaError(
@@ -449,6 +404,7 @@ var Requirement = function (id, name, version, installed) {
* @return Promise<Requirement[]> Array of requirements. Due to implementation, promise is always fulfilled.
*/
module.exports.check_all = function () {
var requirements = [
new Requirement('java', 'Java JDK'),
new Requirement('androidSdk', 'Android SDK'),
@@ -473,7 +429,7 @@ module.exports.check_all = function () {
}, function (err) {
requirement.metadata.reason = err instanceof Error ? err.message : err;
});
}, Promise.resolve()).then(function () {
}, Q()).then(function () {
// When chain is completed, return requirements array to upstream API
return requirements;
});

View File

@@ -17,10 +17,10 @@
under the License.
*/
const fs = require('fs');
const path = require('path');
const propertiesParser = require('properties-parser');
const events = require('cordova-common').events;
let fs = require('fs');
let path = require('path');
let propertiesParser = require('properties-parser');
let events = require('cordova-common').events;
class GradlePropertiesParser {
/**
@@ -36,9 +36,8 @@ class GradlePropertiesParser {
// to allow dex in process
'org.gradle.jvmargs': '-Xmx2048m',
// Android X
'android.useAndroidX': 'false',
'android.enableJetifier': 'false'
// allow NDK to be used - required by Gradle 1.5 plugin
'android.useDeprecatedNdk': 'true'
// Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
// 'org.gradle.parallel': 'true'
@@ -82,62 +81,17 @@ class GradlePropertiesParser {
_configureProperties (properties) {
// Iterate though the properties and set only if missing.
Object.keys(properties).forEach(key => {
const value = this.gradleFile.get(key);
let value = this.gradleFile.get(key);
if (!value) {
// Handles the case of adding missing defaults or new properties that are missing.
events.emit('verbose', `[Gradle Properties] Appending configuration item: ${key}=${properties[key]}`);
this.gradleFile.set(key, properties[key]);
} else if (value !== properties[key]) {
if (this._defaults[key] && this._defaults[key] !== properties[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]}"`);
}
// We will set the new value in either case.
this.gradleFile.set(key, properties[key]);
events.emit('info', `[Gradle Properties] Detected Gradle property "${key}" with the value of "${value}", Cordova's recommended value is "${properties[key]}"`);
}
});
}
_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,3 +1,5 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -17,11 +19,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;
@@ -35,7 +37,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 execa('killall', ['adb']).then(function () {
return spawn('killall', ['adb']).then(function () {
events.emit('verbose', 'Restarting adb to see if more devices are detected.');
return Adb.devices();
}, function () {

View File

@@ -1,3 +1,5 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -17,8 +19,6 @@
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,49 +26,53 @@ 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
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';
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';
function forgivingWhichSync (cmd) {
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);
try {
return fs.realpathSync(shelljs.which(cmd));
} catch (e) {
return '';
}
}
module.exports.list_images_using_avdmanager = function () {
return execa('avdmanager', ['list', 'avd']).then(({ stdout: output }) => {
return superspawn.spawn('avdmanager', ['list', 'avd']).then(function (output) {
var response = output.split('\n');
var emulator_list = [];
for (var i = 1; i < response.length; i++) {
// 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
@@ -76,24 +80,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);
@@ -102,40 +106,41 @@ 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 execa('android', ['list', 'avd']).then(({ stdout: output }) => {
return superspawn.spawn('android', ['list', 'avd']).then(function (output) {
var response = output.split('\n');
var emulator_list = [];
for (var i = 1; i < response.length; i++) {
// 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);
@@ -144,6 +149,7 @@ 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;
});
@@ -223,7 +229,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 execa('android', ['list', 'targets'], { cwd: os.tmpdir() }).then(({ stdout: output }) => {
return superspawn.spawn('android', ['list', 'targets'], { cwd: os.tmpdir() }).then(function (output) {
var target_out = output.split('\n');
var targets = [];
for (var i = target_out.length; i >= 0; i--) {
@@ -285,10 +291,11 @@ 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(which.sync('emulator'));
var emulator_dir = path.dirname(shelljs.which('emulator'));
var args = ['-avd', emulatorId, '-port', port];
// Don't wait for it to finish, since the emulator will probably keep running for a long time.
execa('emulator', args, { stdio: 'inherit', detached: true, cwd: emulator_dir })
child_process
.spawn('emulator', args, { stdio: 'inherit', detached: true, cwd: emulator_dir })
.unref();
// wait for emulator to start
@@ -352,8 +359,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, 'getprop sys.boot_completed').then(function (output) {
if (output.match(/1/)) {
return Adb.shell(emulator_id, 'ps').then(function (output) {
if (output.match(/android\.process\.acore/)) {
return true;
} else if (time_remaining === 0) {
return false;
@@ -380,22 +387,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 execa('android', ['create', 'avd', '--name', name, '--target', target]).then(null, function (error) {
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', target]).then(null, function (error) {
console.error('ERROR : Failed to create emulator image : ');
console.error(' Do you have the latest android targets including ' + target + '?');
console.error(error.message);
console.error(error);
});
} 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 execa('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]]).then(function () {
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]]).then(function () {
// TODO: This seems like another error case, even though it always happens.
console.error('ERROR : Unable to create an avd emulator, no targets found.');
console.error('Ensure you have targets available by running the "android" command');
return Promise.reject(new CordovaError());
}, function (error) {
console.error('ERROR : Failed to create emulator image : ');
console.error(error.message);
console.error(error);
});
}
};
@@ -425,6 +432,7 @@ 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');
@@ -448,6 +456,7 @@ 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(),
@@ -465,21 +474,24 @@ module.exports.install = function (givenTarget, buildResults) {
function adbInstallWithOptions (target, apk, opts) {
events.emit('verbose', 'Installing apk ' + apk + ' on ' + target + '...');
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.';
}
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.';
}
throw new CordovaError('Failed to install apk to emulator: ' + stdout);
}
reject(new CordovaError('Failed to install apk to emulator: ' + stdout));
} else resolve(stdout);
});
});
}
@@ -506,6 +518,7 @@ 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,3 +1,5 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -19,7 +21,8 @@
var path = require('path');
var os = require('os');
var execa = require('execa');
var Q = require('q');
var child_process = require('child_process');
var ROOT = path.join(__dirname, '..', '..');
/*
@@ -27,7 +30,8 @@ var ROOT = path.join(__dirname, '..', '..');
* Returns a promise.
*/
module.exports.run = function () {
var adb = execa('adb', ['logcat'], { cwd: os.tmpdir(), stderr: 'inherit' });
var d = Q.defer();
var adb = child_process.spawn('adb', ['logcat'], { cwd: os.tmpdir() });
adb.stdout.on('data', function (data) {
var lines = data ? data.toString().split('\n') : [];
@@ -35,7 +39,14 @@ module.exports.run = function () {
console.log(out.join('\n'));
});
return adb;
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;
};
module.exports.help = function () {

View File

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

View File

@@ -16,10 +16,12 @@
specific language governing permissions and limitations
under the License.
*/
/* eslint no-useless-escape: 0 */
var fs = require('fs-extra');
var Q = require('q');
var fs = require('fs');
var path = require('path');
const nopt = require('nopt');
var shell = require('shelljs');
var events = require('cordova-common').events;
var AndroidManifest = require('./AndroidManifest');
var checkReqs = require('./check_reqs');
@@ -30,25 +32,12 @@ 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());
@@ -56,32 +45,15 @@ module.exports.prepare = function (cordovaProject, options) {
// Get the min SDK version from config.xml
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');
const gradlePropertiesUserConfig = {};
let 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';
}
// Both 'useAndroidX' and 'enableJetifier' are linked together.
if (androidXEnabled) {
gradlePropertiesUserConfig['android.useAndroidX'] = androidXEnabled;
gradlePropertiesUserConfig['android.enableJetifier'] = androidXEnabled;
}
const gradlePropertiesParser = new GradlePropertiesParser(this.locations.root);
let 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 Promise.resolve(updateWww(cordovaProject, this.locations)).then(function () {
return Q.when(updateWww(cordovaProject, this.locations)).then(function () {
// update project according to config.xml changes.
return updateProjectAccordingTo(self._config, self.locations);
}).then(function () {
@@ -101,13 +73,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 Promise.resolve();
return Q();
}
var projectConfig = new ConfigParser(this.locations.configXml);
var self = this;
return Promise.resolve().then(function () {
return Q().then(function () {
cleanWww(projectRoot, self.locations);
cleanIcons(projectRoot, projectConfig, path.relative(projectRoot, self.locations.res));
cleanSplashes(projectRoot, projectConfig, path.relative(projectRoot, self.locations.res));
@@ -134,7 +106,7 @@ function updateConfigFilesFrom (sourceConfig, configMunger, locations) {
// First cleanup current config and merge project's one into own
// Overwrite platform config.xml with defaults.xml.
fs.copySync(locations.defaultConfigXml, locations.configXml);
shell.cp('-f', locations.defaultConfigXml, locations.configXml);
// Then apply config changes from global munge to all config files
// in project (including project's config)
@@ -210,11 +182,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');
@@ -233,13 +205,15 @@ function updateProjectAccordingTo (platformConfig, locations) {
manifest.setVersionName(platformConfig.version())
.setVersionCode(platformConfig.android_versionCode() || default_versionCode(platformConfig.version()))
.setPackageId(androidPkgName)
.setMinSdkVersion(platformConfig.getPreference('android-minSdkVersion', 'android'))
.setMaxSdkVersion(platformConfig.getPreference('android-maxSdkVersion', 'android'))
.setTargetSdkVersion(platformConfig.getPreference('android-targetSdkVersion', 'android'))
.write();
// Java file paths shouldn't be hard coded
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;
var javaPattern = path.join(locations.javaSrc, manifestId.replace(/\./g, '/'), '*.java');
var java_files = shell.ls(javaPattern).filter(function (f) {
return shell.grep(/extends\s+CordovaActivity/g, f);
});
if (java_files.length === 0) {
@@ -248,24 +222,18 @@ function updateProjectAccordingTo (platformConfig, locations) {
events.emit('log', 'Multiple candidate Java files that extend CordovaActivity found. Guessing at the first one, ' + java_files[0]);
}
const destFile = java_files[0];
// 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 + ';');
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);
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
fs.removeSync(java_files[0]);
shell.rm('-Rf', java_files[0]);
// remove any empty directories
var currentDir = path.dirname(java_files[0]);
var sourcesRoot = path.resolve(locations.root, 'src');
@@ -365,7 +333,7 @@ function cleanSplashes (projectRoot, projectConfig, platformResourcesDir) {
}
function updateIcons (cordovaProject, platformResourcesDir) {
const icons = cordovaProject.projectConfig.getIcons('android');
let icons = cordovaProject.projectConfig.getIcons('android');
// Skip if there are no app defined icons in config.xml
if (icons.length === 0) {
@@ -375,14 +343,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.
const errorMissingAttributes = [];
const errorLegacyIconNeeded = [];
let errorMissingAttributes = [];
let 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));
}
@@ -391,10 +359,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));
@@ -404,7 +372,7 @@ function updateIcons (cordovaProject, platformResourcesDir) {
}
});
const errorMessage = [];
let 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.');
}
@@ -427,7 +395,7 @@ function updateIcons (cordovaProject, platformResourcesDir) {
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher.xml')
);
const preparedIcons = prepareIcons(icons);
let preparedIcons = prepareIcons(icons);
if (hasAdaptive) {
resourceMap = updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
@@ -440,8 +408,8 @@ function updateIcons (cordovaProject, platformResourcesDir) {
}
function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformResourcesDir) {
const android_icons = preparedIcons.android_icons;
const default_icon = preparedIcons.default_icon;
let android_icons = preparedIcons.android_icons;
let 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.
@@ -450,18 +418,13 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
let targetPathBackground;
let targetPathForeground;
for (const density in android_icons) {
for (let 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
@@ -495,7 +458,7 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
<foreground android:drawable="` + foregroundVal + `" />
</adaptive-icon>`;
const launcherXmlPath = path.join(platformResourcesDir, 'mipmap-' + density + '-v26', 'ic_launcher.xml');
let 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];
@@ -539,8 +502,8 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
}
function updateIconResourceForLegacy (preparedIcons, resourceMap, platformResourcesDir) {
const android_icons = preparedIcons.android_icons;
const default_icon = preparedIcons.default_icon;
let android_icons = preparedIcons.android_icons;
let 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.
@@ -569,7 +532,7 @@ function prepareIcons (icons) {
192: 'xxxhdpi'
};
const android_icons = {};
let android_icons = {};
let default_icon;
// find the best matching icon for a given density or size
@@ -599,8 +562,8 @@ function prepareIcons (icons) {
if (!size && !icon.density) {
if (default_icon) {
const found = {};
const favor = {};
let found = {};
let favor = {};
// populating found icon.
if (icon.background && icon.foreground) {
@@ -643,7 +606,7 @@ function cleanIcons (projectRoot, projectConfig, platformResourcesDir) {
return;
}
const resourceMap = Object.assign(
let resourceMap = Object.assign(
{},
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher.png'),
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_foreground.png'),
@@ -663,10 +626,9 @@ 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) {
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);
var pathMap = {};
shell.ls(path.join(rootDir, subDir, type + '-*')).forEach(function (drawableFolder) {
var imagePath = path.join(subDir, path.basename(drawableFolder), resourceName);
pathMap[imagePath] = null;
});
return pathMap;
@@ -704,7 +666,8 @@ function cleanFileResources (projectRoot, projectConfig, platformDir) {
});
FileUpdater.updatePaths(
resourceMap, { rootDir: projectRoot, all: true }, logFileOp);
resourceMap, {
rootDir: projectRoot, all: true }, logFileOp);
}
}

View File

@@ -1,3 +1,5 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -32,6 +34,7 @@ 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,3 +1,5 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -20,8 +22,8 @@
var path = require('path');
var emulator = require('./emulator');
var device = require('./device');
var PackageType = require('./PackageType');
const { CordovaError, events } = require('cordova-common');
var Q = require('q');
var events = require('cordova-common').events;
function getInstallTarget (runOptions) {
var install_target;
@@ -52,7 +54,7 @@ module.exports.run = function (runOptions) {
var self = this;
var install_target = getInstallTarget(runOptions);
return Promise.resolve().then(function () {
return Q().then(function () {
if (!install_target) {
// no target given, deploy to device if available, otherwise use the emulator.
return device.list().then(function (device_list) {
@@ -94,7 +96,7 @@ module.exports.run = function (runOptions) {
});
}
}
return Promise.reject(new CordovaError(`Target '${install_target}' not found, unable to run project`));
return Q.reject('Target \'' + install_target + '\' not found, unable to run project');
});
});
});
@@ -102,14 +104,6 @@ module.exports.run = function (runOptions) {
return new Promise((resolve) => {
const builder = require('./builders/builders').getBuilder();
const buildOptions = require('./build').parseBuildOptions(runOptions, null, self.root);
// Android app bundles cannot be deployed directly to the device
if (buildOptions.packageType === PackageType.BUNDLE) {
const packageTypeErrorMessage = 'Package type "bundle" is not supported during cordova run.';
events.emit('error', packageTypeErrorMessage);
throw packageTypeErrorMessage;
}
resolve(builder.fetchBuildResults(buildOptions.buildType, buildOptions.arch));
}).then(function (buildResults) {
if (resolvedTarget && resolvedTarget.isEmulator) {

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,97 +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.
*/
/*
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().then(function () {
reqs.run().done(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,6 +19,11 @@
under the License.
*/
const Api = require('./Api');
// Coho updates this line:
var VERSION = "8.0.0";
console.log(Api.version());
module.exports.version = VERSION;
if (!module.parent) {
console.log(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"
@@ -37,11 +37,13 @@
android:launchMode="singleTop"
android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
android:windowSoftInputMode="adjustResize"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode">
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale">
<intent-filter android:label="@string/launcher_name">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="__APILEVEL__"/>
</manifest>

View File

@@ -19,31 +19,7 @@
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()
@@ -51,32 +27,7 @@ buildscript {
}
dependencies {
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
}
classpath 'com.android.tools.build:gradle:3.3.0'
}
}
@@ -89,7 +40,7 @@ allprojects {
}
task wrapper(type: Wrapper) {
gradleVersion = '6.5'
gradleVersion = '4.10.3'
}
// Configuration properties. Set these via environment variables, build-extras.gradle, or gradle.properties.
@@ -113,14 +64,6 @@ ext {
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
@@ -160,12 +103,10 @@ if (hasBuildExtras2) {
}
// Set property defaults after extension .gradle files.
ext.cdvCompileSdkVersion = cdvCompileSdkVersion == null ? (
defaultCompileSdkVersion == null
? privateHelpers.getProjectTarget()
: defaultCompileSdkVersion
) : Integer.parseInt('' + cdvCompileSdkVersion);
if (ext.cdvCompileSdkVersion == null) {
ext.cdvCompileSdkVersion = privateHelpers.getProjectTarget()
//ext.cdvCompileSdkVersion = project.ext.defaultCompileSdkVersion
}
if (ext.cdvBuildToolsVersion == null) {
ext.cdvBuildToolsVersion = privateHelpers.findLatestInstalledBuildTools()
//ext.cdvBuildToolsVersion = project.ext.defaultBuildToolsVersion
@@ -180,14 +121,7 @@ if (ext.cdvReleaseSigningPropertiesFile == null && file('../release-signing.prop
// Cast to appropriate types.
ext.cdvBuildMultipleApks = cdvBuildMultipleApks == null ? false : cdvBuildMultipleApks.toBoolean();
ext.cdvVersionCodeForceAbiDigit = cdvVersionCodeForceAbiDigit == null ? false : cdvVersionCodeForceAbiDigit.toBoolean();
// minSdkVersion, maxSdkVersion and targetSdkVersion
ext.cdvMinSdkVersion = cdvMinSdkVersion == null ? defaultMinSdkVersion : Integer.parseInt('' + cdvMinSdkVersion)
if (cdvMaxSdkVersion != null) {
ext.cdvMaxSdkVersion = Integer.parseInt('' + cdvMaxSdkVersion)
}
ext.cdvTargetSdkVersion = cdvTargetSdkVersion == null ? defaultTargetSdkVersion : Integer.parseInt('' + cdvTargetSdkVersion)
ext.cdvVersionCode = cdvVersionCode == null ? null : Integer.parseInt('' + cdvVersionCode)
def computeBuildTargetName(debugBuild) {
@@ -217,8 +151,6 @@ task cdvPrintProps {
println('cdvVersionCode=' + cdvVersionCode)
println('cdvVersionCodeForceAbiDigit=' + cdvVersionCodeForceAbiDigit)
println('cdvMinSdkVersion=' + cdvMinSdkVersion)
println('cdvMaxSdkVersion=' + cdvMaxSdkVersion)
println('cdvTargetSdkVersion=' + cdvTargetSdkVersion)
println('cdvBuildMultipleApks=' + cdvBuildMultipleApks)
println('cdvReleaseSigningPropertiesFile=' + cdvReleaseSigningPropertiesFile)
println('cdvDebugSigningPropertiesFile=' + cdvDebugSigningPropertiesFile)
@@ -238,14 +170,6 @@ android {
if (cdvMinSdkVersion != null) {
minSdkVersion cdvMinSdkVersion
}
if (cdvMaxSdkVersion != null) {
maxSdkVersion cdvMaxSdkVersion
}
if(cdvTargetSdkVersion != null) {
targetSdkVersion cdvTargetSdkVersion
}
}
lintOptions {
@@ -329,10 +253,6 @@ android {
if (cdvDebugSigningPropertiesFile) {
addSigningProps(cdvDebugSigningPropertiesFile, signingConfigs.debug)
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
}
/*
@@ -344,11 +264,6 @@ 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"))
@@ -414,7 +329,3 @@ 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
// 538a985db128858c0a0eb4dd40fb9c8e5433fc94
// 882658ab17740dbdece764e68c1f1f1f44fe3f9d
/*
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,8 +19,9 @@
under the License.
*/
;(function() {
var PLATFORM_VERSION_BUILD_LABEL = '9.0.0';
var PLATFORM_VERSION_BUILD_LABEL = '8.0.0';
// file: src/scripts/require.js
var require;
var define;
@@ -50,10 +51,10 @@ var define;
require = function (id) {
if (!modules[id]) {
throw new Error('module ' + id + ' not found');
throw 'module ' + id + ' not found';
} else if (id in inProgressModules) {
var cycle = requireStack.slice(inProgressModules[id]).join('->') + '->' + id;
throw new Error('Cycle in require graph: ' + cycle);
throw 'Cycle in require graph: ' + cycle;
}
if (modules[id].factory) {
try {
@@ -69,8 +70,8 @@ var define;
};
define = function (id, factory) {
if (Object.prototype.hasOwnProperty.call(modules, id)) {
throw new Error('module ' + id + ' already defined');
if (modules[id]) {
throw 'module ' + id + ' already defined';
}
modules[id] = {
@@ -97,7 +98,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)) {
if (window.cordova && !(window.cordova instanceof HTMLElement)) { // eslint-disable-line no-undef
throw new Error('cordova already defined');
}
@@ -162,7 +163,7 @@ function createEvent (type, data) {
event.initEvent(type, false, false);
if (data) {
for (var i in data) {
if (Object.prototype.hasOwnProperty.call(data, i)) {
if (data.hasOwnProperty(i)) {
event[i] = data[i];
}
}
@@ -170,6 +171,7 @@ function createEvent (type, data) {
return event;
}
/* eslint-disable no-undef */
var cordova = {
define: define,
require: require,
@@ -177,6 +179,8 @@ 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.
*/
@@ -195,25 +199,15 @@ 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
@@ -236,7 +230,6 @@ var cordova = {
document.dispatchEvent(evt);
}
},
fireWindowEvent: function (type, data) {
var evt = createEvent(type, data);
if (typeof windowEventHandlers[type] !== 'undefined') {
@@ -310,11 +303,12 @@ var cordova = {
}
} catch (err) {
var msg = 'Error in ' + (isSuccess ? 'Success' : 'Error') + ' callbackId: ' + callbackId + ' : ' + err;
cordova.fireWindowEvent('cordovacallbackerror', { message: msg, error: err });
console && console.log && console.log(msg);
console && console.log && err.stack && console.log(err.stack);
cordova.fireWindowEvent('cordovacallbackerror', { 'message': msg });
throw err;
}
},
addConstructor: function (func) {
channel.onCordovaReady.subscribe(function () {
try {
@@ -330,7 +324,7 @@ module.exports = cordova;
});
// file: ../cordova-android/cordova-js-src/android/nativeapiprovider.js
// file: /Users/erisu/git/apache/cordova/cordova-android/cordova-js-src/android/nativeapiprovider.js
define("cordova/android/nativeapiprovider", function(require, exports, module) {
/**
@@ -341,19 +335,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: ../cordova-android/cordova-js-src/android/promptbasednativeapi.js
// file: /Users/erisu/git/apache/cordova/cordova-android/cordova-js-src/android/promptbasednativeapi.js
define("cordova/android/promptbasednativeapi", function(require, exports, module) {
/**
@@ -362,13 +356,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);
}
};
@@ -383,47 +377,18 @@ 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 (/\(\s*([^)]*?)\s*\)/).exec(callee)[1].split(/\s*,\s*/)[argIndex];
return (/.*?\((.*?)\)/).exec(callee)[1].split(', ')[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;
@@ -479,7 +444,7 @@ base64.fromArrayBuffer = function (arrayBuffer) {
};
base64.toArrayBuffer = function (str) {
var decodedStr = atob(str);
var decodedStr = typeof atob !== 'undefined' ? atob(str) : Buffer.from(str, 'base64').toString('binary'); // eslint-disable-line no-undef
var arrayBuffer = new ArrayBuffer(decodedStr.length);
var array = new Uint8Array(arrayBuffer);
for (var i = 0, len = decodedStr.length; i < len; i++) {
@@ -541,13 +506,14 @@ var utils = require('cordova/utils');
function each (objects, func, context) {
for (var prop in objects) {
if (Object.prototype.hasOwnProperty.call(objects, prop)) {
if (objects.hasOwnProperty(prop)) {
func.apply(context, [objects[prop], prop]);
}
}
}
function clobber (obj, key, value) {
exports.replaceHookForTesting(obj, key);
var needsProperty = false;
try {
obj[key] = value;
@@ -621,7 +587,7 @@ function include (parent, objects, clobber, merge) {
*/
function recursiveMerge (target, src) {
for (var prop in src) {
if (Object.prototype.hasOwnProperty.call(src, prop)) {
if (src.hasOwnProperty(prop)) {
if (target.prototype && target.prototype.constructor === target) {
// If the target object is a constructor override off prototype.
clobber(target.prototype, prop, src[prop]);
@@ -647,6 +613,7 @@ exports.buildIntoAndMerge = function (objects, target) {
};
exports.recursiveMerge = recursiveMerge;
exports.assignOrWrapInDeprecateGetter = assignOrWrapInDeprecateGetter;
exports.replaceHookForTesting = function () {};
});
@@ -727,14 +694,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.
*/
@@ -855,6 +822,7 @@ 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) {
@@ -911,7 +879,7 @@ module.exports = channel;
});
// file: ../cordova-android/cordova-js-src/exec.js
// file: /Users/erisu/git/apache/cordova/cordova-android/cordova-js-src/exec.js
define("cordova/exec", function(require, exports, module) {
/**
@@ -928,38 +896,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');
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 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 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
@@ -978,21 +946,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++;
var argsJson = JSON.stringify(args);
var callbackId = service + cordova.callbackId++,
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);
@@ -1003,16 +971,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.
@@ -1026,15 +994,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.
@@ -1054,19 +1022,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;
}
@@ -1077,32 +1045,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);
@@ -1115,15 +1083,14 @@ 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);
@@ -1133,11 +1100,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;
@@ -1150,7 +1117,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;
}
@@ -1163,9 +1130,9 @@ function processMessages () {
}
}
function popMessageFromQueue () {
function popMessageFromQueue() {
var messageBatch = messagesFromNative.shift();
if (messageBatch === '*') {
if (messageBatch == '*') {
return '*';
}
@@ -1221,6 +1188,7 @@ 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];
@@ -1240,6 +1208,34 @@ 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 () {}
@@ -1305,6 +1301,7 @@ channel.join(function () {
channel.join(function () {
require('cordova').fireDocumentEvent('deviceready');
}, channel.deviceReadyChannelsArray);
}, platformInitChannelsArray);
});
@@ -1313,7 +1310,7 @@ channel.join(function () {
define("cordova/modulemapper", function(require, exports, module) {
var builder = require('cordova/builder');
var moduleMap = define.moduleMap;
var moduleMap = define.moduleMap; // eslint-disable-line no-undef
var symbolList;
var deprecationMap;
@@ -1353,9 +1350,12 @@ function prepareNamespace (symbolPath, context) {
if (!symbolPath) {
return context;
}
return symbolPath.split('.').reduce(function (cur, part) {
return (cur[part] = cur[part] || {});
}, 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;
}
exports.mapModules = function (context) {
@@ -1406,7 +1406,7 @@ exports.reset();
});
// file: ../cordova-android/cordova-js-src/platform.js
// file: /Users/erisu/git/apache/cordova/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');
var cordova = require('cordova');
var exec = require('cordova/exec');
var modulemapper = require('cordova/modulemapper');
bootstrap: function() {
var channel = require('cordova/channel'),
cordova = require('cordova'),
exec = require('cordova/exec'),
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,54 +1466,57 @@ 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) {
// 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]);
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;
}
msg.pendingResult.result = res;
}
// 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);
// 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: ../cordova-android/cordova-js-src/plugin/android/app.js
// file: /Users/erisu/git/apache/cordova/cordova-android/cordova-js-src/plugin/android/app.js
define("cordova/plugin/android/app", function(require, exports, module) {
var exec = require('cordova/exec');
@@ -1523,8 +1526,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", []);
},
/**
@@ -1541,31 +1544,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", []);
},
/**
@@ -1577,8 +1580,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]);
},
/**
@@ -1592,15 +1595,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", []);
}
};
@@ -1625,11 +1628,11 @@ exports.injectScript = function (url, onload, onerror) {
function injectIfNecessary (id, url, onload, onerror) {
onerror = onerror || onload;
if (id in define.moduleMap) {
if (id in define.moduleMap) { // eslint-disable-line no-undef
onload();
} else {
exports.injectScript(url, function () {
if (id in define.moduleMap) {
if (id in define.moduleMap) { // eslint-disable-line no-undef
onload();
} else {
onerror();
@@ -1640,7 +1643,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++) {
for (var i = 0, module; module = moduleList[i]; i++) { // eslint-disable-line no-cond-assign
if (module.clobbers && module.clobbers.length) {
for (var j = 0; j < module.clobbers.length; j++) {
modulemapper.clobbers(module.id, module.clobbers[j]);
@@ -1823,11 +1826,10 @@ utils.clone = function (obj) {
retVal = {};
for (i in obj) {
// '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') {
// 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
retVal[i] = utils.clone(obj[i]);
}
}
@@ -1877,6 +1879,7 @@ 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;
@@ -1899,6 +1902,7 @@ 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="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
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
}
}

View File

@@ -0,0 +1,311 @@
/*
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
}
// 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)
ext.cdvVersionCode = cdvVersionCode == null ? null : Integer.parseInt('' + cdvVersionCode)
def computeBuildTargetName(debugBuild) {
def ret = 'assemble'
if (cdvBuildMultipleApks && cdvBuildArch) {
def arch = cdvBuildArch == 'arm' ? 'armv7' : cdvBuildArch
ret += '' + arch.toUpperCase().charAt(0) + arch.substring(1);
}
return ret + (debugBuild ? 'Debug' : 'Release')
}
// Make cdvBuild a task that depends on the debug/arch-sepecific task.
task cdvBuildDebug
cdvBuildDebug.dependsOn {
return computeBuildTargetName(true)
}
task cdvBuildRelease
cdvBuildRelease.dependsOn {
return computeBuildTargetName(false)
}
task cdvPrintProps << {
println('cdvCompileSdkVersion=' + cdvCompileSdkVersion)
println('cdvBuildToolsVersion=' + cdvBuildToolsVersion)
println('cdvVersionCode=' + cdvVersionCode)
println('cdvMinSdkVersion=' + cdvMinSdkVersion)
println('cdvBuildMultipleApks=' + cdvBuildMultipleApks)
println('cdvReleaseSigningPropertiesFile=' + cdvReleaseSigningPropertiesFile)
println('cdvDebugSigningPropertiesFile=' + cdvDebugSigningPropertiesFile)
println('cdvBuildArch=' + cdvBuildArch)
println('computedVersionCode=' + android.defaultConfig.versionCode)
android.productFlavors.each { flavor ->
println('computed' + flavor.name.capitalize() + 'VersionCode=' + flavor.versionCode)
}
}
android {
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
}
defaultConfig {
versionCode cdvVersionCode ?: new BigInteger("" + privateHelpers.extractIntFromManifest("versionCode"))
applicationId privateHelpers.extractStringFromManifest("package")
if (cdvMinSdkVersion != null) {
minSdkVersion cdvMinSdkVersion
}
}
lintOptions {
abortOnError false;
}
compileSdkVersion cdvCompileSdkVersion
buildToolsVersion cdvBuildToolsVersion
if (Boolean.valueOf(cdvBuildMultipleApks)) {
productFlavors {
armv7 {
versionCode defaultConfig.versionCode*10 + 2
ndk {
abiFilters "armeabi-v7a", ""
}
}
x86 {
versionCode defaultConfig.versionCode*10 + 4
ndk {
abiFilters "x86", ""
}
}
all {
ndk {
abiFilters "all", ""
}
}
}
}
/*
ELSE NOTHING! DON'T MESS WITH THE VERSION CODE IF YOU DON'T HAVE TO!
else if (!cdvVersionCode) {
def minSdkVersion = cdvMinSdkVersion ?: privateHelpers.extractIntFromManifest("minSdkVersion")
// Vary versionCode by the two most common API levels:
// 14 is ICS, which is the lowest API level for many apps.
// 20 is Lollipop, which is the lowest API level for the updatable system webview.
if (minSdkVersion >= 20) {
defaultConfig.versionCode += 9
} else if (minSdkVersion >= 14) {
defaultConfig.versionCode += 8
}
}
*/
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
targetCompatibility JavaVersion.VERSION_1_6
}
if (cdvReleaseSigningPropertiesFile) {
signingConfigs {
release {
// These must be set or Gradle will complain (even if they are overridden).
keyAlias = ""
keyPassword = "__unset" // And these must be set to non-empty in order to have the signing step added to the task graph.
storeFile = null
storePassword = "__unset"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
addSigningProps(cdvReleaseSigningPropertiesFile, signingConfigs.release)
}
if (cdvDebugSigningPropertiesFile) {
addSigningProps(cdvDebugSigningPropertiesFile, signingConfigs.debug)
}
}
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()
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 B

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 B

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 B

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 B

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

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,7 +34,4 @@ 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) }).catch(err => {
console.error(err);
process.exitCode = 1;
});
Api.updatePlatform(args.argv.remain[0], { link: (args.link || args.shared) }).done();

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');
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 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 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++;
var argsJson = JSON.stringify(args);
var callbackId = service + cordova.callbackId++,
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,15 +220,14 @@ 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);
@@ -238,11 +237,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;
@@ -255,7 +254,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;
}
@@ -268,9 +267,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');
var cordova = require('cordova');
var exec = require('cordova/exec');
var modulemapper = require('cordova/modulemapper');
bootstrap: function() {
var channel = require('cordova/channel'),
cordova = require('cordova'),
exec = require('cordova/exec'),
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,47 +76,50 @@ 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) {
// 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]);
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;
}
msg.pendingResult.result = res;
}
// 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);
// 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);
}
}

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