mirror of
https://github.com/apache/cordova-android.git
synced 2026-04-04 00:02:03 +08:00
Compare commits
56 Commits
11.0.x
...
rel/13.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2143045d4e | ||
|
|
3c5df42df5 | ||
|
|
c2f315c0ff | ||
|
|
89a0a72da5 | ||
|
|
ed8e5d2f0a | ||
|
|
7fa4a65d0a | ||
|
|
b773ae48f4 | ||
|
|
ebf0b105a3 | ||
|
|
9261b29cf2 | ||
|
|
90e74befc7 | ||
|
|
97806db463 | ||
|
|
4742358601 | ||
|
|
e61e271f5b | ||
|
|
0c805a0a8e | ||
|
|
a7cd4227a4 | ||
|
|
c9e7c59986 | ||
|
|
94234d988e | ||
|
|
b104554877 | ||
|
|
7da13ccf77 | ||
|
|
cb48147398 | ||
|
|
6f6717afbd | ||
|
|
3343c3bb34 | ||
|
|
a62f699380 | ||
|
|
7efe90faac | ||
|
|
5b546a27e6 | ||
|
|
2252c09a49 | ||
|
|
3a9c87d3b8 | ||
|
|
a9d4d4ebd2 | ||
|
|
841710edf7 | ||
|
|
016018513e | ||
|
|
a78fad1783 | ||
|
|
b91639dbb5 | ||
|
|
c2013439bc | ||
|
|
d4bfd5079b | ||
|
|
dbddbf253b | ||
|
|
04723eb8f3 | ||
|
|
862d33694e | ||
|
|
fe3940a73c | ||
|
|
81c678c58d | ||
|
|
bfe086a2d7 | ||
|
|
8fb707567a | ||
|
|
992a60a434 | ||
|
|
2318ef58ad | ||
|
|
968bd85cc3 | ||
|
|
9ef3ee9539 | ||
|
|
5347054efb | ||
|
|
3340e98519 | ||
|
|
ce19a3b445 | ||
|
|
d02f8eafe8 | ||
|
|
56d4b8312b | ||
|
|
80f232aa79 | ||
|
|
954d3e0e75 | ||
|
|
8a1ffeeafd | ||
|
|
7793db97cc | ||
|
|
248257bd37 | ||
|
|
60e3803c67 |
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -23,7 +23,7 @@
|
||||
*.scm text
|
||||
*.sql text
|
||||
*.sh text
|
||||
*.bat text
|
||||
*.bat text eol=crlf
|
||||
|
||||
# templates
|
||||
*.ejs text
|
||||
@@ -92,3 +92,4 @@ AUTHORS text
|
||||
*.woff binary
|
||||
*.pyc binary
|
||||
*.pdf binary
|
||||
*.jar binary
|
||||
|
||||
37
.github/workflows/ci.yml
vendored
37
.github/workflows/ci.yml
vendored
@@ -27,22 +27,19 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [14.x, 16.x, 18.x]
|
||||
node-version: [16.x, 18.x, 20.x, 22.x]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: set up JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
|
||||
- name: Environment Information
|
||||
run: |
|
||||
@@ -50,6 +47,21 @@ jobs:
|
||||
npm --version
|
||||
gradle --version
|
||||
|
||||
# "bin/templates/platform_www/cordova.js" is ignored because it is a generated file.
|
||||
# It contains mixed content from the npm package "cordova-js" and "./cordova-js-src".
|
||||
# The report might not be resolvable because of the external package.
|
||||
# If the report is related to this repository, it would be detected when scanning "./cordova-js-src".
|
||||
- uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: javascript, java-kotlin
|
||||
queries: security-and-quality
|
||||
config: |
|
||||
paths-ignore:
|
||||
- coverage
|
||||
- node_modules
|
||||
- templates/project/assets/www/cordova.js
|
||||
- test/androidx/app/src/main/assets/www/cordova.js
|
||||
|
||||
- name: npm install and test
|
||||
run: |
|
||||
npm i
|
||||
@@ -57,6 +69,11 @@ jobs:
|
||||
env:
|
||||
CI: true
|
||||
|
||||
- uses: codecov/codecov-action@v3
|
||||
- uses: github/codeql-action/analyze@v3
|
||||
|
||||
- uses: codecov/codecov-action@v4
|
||||
if: success()
|
||||
with:
|
||||
fail_ci_if_error: true
|
||||
name: ${{ runner.os }} node.js ${{ matrix.node-version }}
|
||||
token: ${{ secrets.CORDOVA_CODECOV_TOKEN }}
|
||||
fail_ci_if_error: false
|
||||
|
||||
45
.github/workflows/release-audit.yml
vendored
Normal file
45
.github/workflows/release-audit.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
# 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: Release Auditing
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Audit Licenses
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Checkout project
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Check license headers
|
||||
- uses: erisu/apache-rat-action@555ae80334a535eb6c1f8920b121563a5a985a75
|
||||
|
||||
# Setup environment with node
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
# Install node packages
|
||||
- name: npm install packages
|
||||
run: npm i
|
||||
|
||||
# Check node package licenses
|
||||
- uses: erisu/license-checker-action@e929758f9416f30234ac454fc9054ca4b803871d
|
||||
with:
|
||||
license-config: 'licence_checker.yml'
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -29,12 +29,8 @@ example
|
||||
**/assets/www/cordova.js
|
||||
|
||||
/test/.externalNativeBuild
|
||||
|
||||
/test/androidx/gradle
|
||||
/test/androidx/gradlew
|
||||
/test/androidx/gradlew.bat
|
||||
/test/androidx/cdv-gradle-config.json
|
||||
|
||||
/test/androidx/tools
|
||||
/test/assets/www/.tmp*
|
||||
/test/assets/www/cordova.js
|
||||
/test/bin
|
||||
|
||||
21
.ratignore
21
.ratignore
@@ -1,8 +1,13 @@
|
||||
*.properties
|
||||
templates
|
||||
gen
|
||||
proguard-project.txt
|
||||
spec
|
||||
framework/build
|
||||
ic_launcher.png
|
||||
build
|
||||
\.(.*)
|
||||
(.*).txt
|
||||
coverage
|
||||
fixtures
|
||||
generated
|
||||
gitignore
|
||||
intermediates
|
||||
reports
|
||||
test-results
|
||||
node_modules
|
||||
gradle
|
||||
gradlew
|
||||
gradlew.bat
|
||||
|
||||
3
LICENSE
3
LICENSE
@@ -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-2024 Apache Cordova
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -200,3 +200,4 @@
|
||||
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.
|
||||
|
||||
|
||||
@@ -20,6 +20,97 @@
|
||||
-->
|
||||
## Release Notes for Cordova (Android)
|
||||
|
||||
### 13.0.0 (May 15, 2024)
|
||||
|
||||
**Breaking Changes:**
|
||||
|
||||
* [GH-1678](https://github.com/apache/cordova-android/pull/1678) feat!: API 34 Support
|
||||
* [GH-1543](https://github.com/apache/cordova-android/pull/1543) feat!: bump `kotlin@1.9.24` & drop `kotlin-android-extensions` when kotlin `>=1.8.0`
|
||||
|
||||
**Features:**
|
||||
|
||||
* [GH-1700](https://github.com/apache/cordova-android/pull/1700) feat(splash): Support `SplashScreenBackgroundColor` preference
|
||||
* [GH-1609](https://github.com/apache/cordova-android/pull/1609) feat: add camera intent with file input capture
|
||||
* [GH-1696](https://github.com/apache/cordova-android/pull/1696) feat: Add `ResolveServiceWorkerRequests` preference
|
||||
|
||||
**Chores, Dependencies & CI:**
|
||||
|
||||
* [GH-1677](https://github.com/apache/cordova-android/pull/1677) chore(deps-dev): bump `@babel/traverse` from `7.22.10` to `7.23.2`
|
||||
* [GH-1713](https://github.com/apache/cordova-android/pull/1713) dep: bump npm dependencies 20240515
|
||||
* `semver@7.6.2`
|
||||
* `rewire@7.0.0`
|
||||
* `nopt@7.2.1`
|
||||
* `jasmine@5.1.0`
|
||||
* `fs-extra@11.2.0`
|
||||
* `fast-glob@3.3.2`
|
||||
* `dedent@1.5.3`
|
||||
* `@cordova/eslint-config@5.1.0`
|
||||
* `which@4.0.0`
|
||||
* `properties-parser@0.6.0`
|
||||
* `android-versions@2.0.0`
|
||||
* [GH-1711](https://github.com/apache/cordova-android/pull/1711) ci: Set up CodeQL analysis w/ fixes
|
||||
* [GH-1687](https://github.com/apache/cordova-android/pull/1687) ci(release-audit): add license header and dependency checker
|
||||
* [GH-1703](https://github.com/apache/cordova-android/pull/1703) ci: update `codecov@v4` w/ token
|
||||
|
||||
### 12.0.1 (Aug 23, 2023)
|
||||
|
||||
* [GH-1632](https://github.com/apache/cordova-android/pull/1632) fix(android): `monochrome` checks
|
||||
* [GH-1649](https://github.com/apache/cordova-android/pull/1649) chore: rebuild `package-lock` w/ lint corrections
|
||||
|
||||
### 12.0.0 (May 20, 2023)
|
||||
|
||||
**Breaking:**
|
||||
|
||||
* [GH-1605](https://github.com/apache/cordova-android/pull/1605) fix!: Make `CoreAndroid` plugin instantiate on load
|
||||
* [GH-1539](https://github.com/apache/cordova-android/pull/1539) feat!: bump Gradle 7.6 & AGP 7.4.2
|
||||
* [GH-1571](https://github.com/apache/cordova-android/pull/1571) feat!: bump min SDK to 24
|
||||
* [GH-1538](https://github.com/apache/cordova-android/pull/1538) feat!: bump target sdk & build tools for SDK 33 support
|
||||
* [GH-1540](https://github.com/apache/cordova-android/pull/1540) feat!: bump node engine requirement `>=16.13.0`
|
||||
* [GH-1597](https://github.com/apache/cordova-android/pull/1597) deprecate: `CoreAndroid.getBuildConfigValue`
|
||||
* [GH-1541](https://github.com/apache/cordova-android/pull/1541) dep(npm)!: bump acceptable modules w/ rebuilt `package-lock`
|
||||
* [GH-1566](https://github.com/apache/cordova-android/pull/1566) dep(npm)!: bump `cordova-common@5.0.0`
|
||||
|
||||
**Features:**
|
||||
|
||||
* [GH-1602](https://github.com/apache/cordova-android/pull/1602) feat: add `listTarget` api
|
||||
* [GH-1574](https://github.com/apache/cordova-android/pull/1574) feat: add plugin hooks for `WebViewClient.onRenderProcessGone`
|
||||
* [GH-1594](https://github.com/apache/cordova-android/pull/1594) feat: bump default `kotlin` to version 1.7.21
|
||||
* [GH-1550](https://github.com/apache/cordova-android/pull/1550) feat: add `monochrome` app icon support
|
||||
* [GH-1589](https://github.com/apache/cordova-android/pull/1589) feat: `InspectableWebview` preference
|
||||
* [GH-1568](https://github.com/apache/cordova-android/pull/1568) feat: bump `androidx.appcompat.appcompat` 1.6.1
|
||||
* [GH-1567](https://github.com/apache/cordova-android/pull/1567) feat: bump `androidx.webkit.webkit` 1.6.0
|
||||
* [GH-1545](https://github.com/apache/cordova-android/pull/1545) feat: bump `androidx.webkit.webkit` 1.5.0
|
||||
* [GH-1547](https://github.com/apache/cordova-android/pull/1547) feat: bump `com.google.gms.google-services` 4.3.15
|
||||
* [GH-1546](https://github.com/apache/cordova-android/pull/1546) feat: bump `androidx.core.core-splashscreen` 1.0.0
|
||||
* [GH-1544](https://github.com/apache/cordova-android/pull/1544) feat: bump `androidx.appcompat.appcompat` 1.5.1
|
||||
|
||||
**Fixes:**
|
||||
|
||||
* [GH-1606](https://github.com/apache/cordova-android/pull/1606) fix: Gradle Args parsing
|
||||
* [GH-1575](https://github.com/apache/cordova-android/pull/1575) fix(`BuildHelper`): get package name from `ApplicationInfo`
|
||||
* [GH-1595](https://github.com/apache/cordova-android/pull/1595) fix(test): Native test namespace refactor
|
||||
* [GH-1471](https://github.com/apache/cordova-android/pull/1471) fix: `ANDROID_HOME` is the new default, to check first and give advice
|
||||
* [GH-1573](https://github.com/apache/cordova-android/pull/1573) fix(GH-1432): Default `content` `src` when content tag is missing
|
||||
* [GH-1506](https://github.com/apache/cordova-android/pull/1506) fix: only do fadeout animation if `FadeSplashScreen` is true
|
||||
* [GH-1505](https://github.com/apache/cordova-android/pull/1505) fix: correctly flag API dependency on `AppCompat` for Maven
|
||||
* [GH-1487](https://github.com/apache/cordova-android/pull/1487) fix: Add **Android** prefix to `WindowSplashScreenBrandingImage`
|
||||
* [GH-1489](https://github.com/apache/cordova-android/pull/1489) fix: import type definitions from obsolete `cordova-plugin-splashscreen`
|
||||
|
||||
**Chores, Refactor, Dependencies & CI:**
|
||||
|
||||
* [GH-1493](https://github.com/apache/cordova-android/pull/1493) chore: add `lint:fix` script for fixing lint errors
|
||||
* [GH-1491](https://github.com/apache/cordova-android/pull/1491) chore: Use gradle 7.4.2 distribution url
|
||||
* [GH-1588](https://github.com/apache/cordova-android/pull/1588) refactor: Removed obsolete version code checks
|
||||
* [GH-1492](https://github.com/apache/cordova-android/pull/1492) refactor: replace deprecated `Handler` constructor
|
||||
* [GH-1587](https://github.com/apache/cordova-android/pull/1587) dep: bump npm dependencies
|
||||
* `fs-extra@11.1.1`
|
||||
* `nopt@7.1.0`
|
||||
* `@cordova/eslint-config@5.0.0`
|
||||
* `jasmine@4.6.0`
|
||||
* [GH-1607](https://github.com/apache/cordova-android/pull/1607) ci: Added NodeJS 20.x to the workflow matrix
|
||||
* [GH-1542](https://github.com/apache/cordova-android/pull/1542) ci(workflow): update `codecov/codecov-action@v3`
|
||||
* [GH-1532](https://github.com/apache/cordova-android/pull/1532) ci: update `codecov/codecov-action` reporting format
|
||||
|
||||
### 11.0.0 (Jul 04, 2022)
|
||||
|
||||
**Breaking:**
|
||||
|
||||
@@ -18,5 +18,6 @@
|
||||
under the License.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.apache.cordova" android:versionName="1.0" android:versionCode="1">
|
||||
android:versionName="1.0"
|
||||
android:versionCode="1">
|
||||
</manifest>
|
||||
|
||||
@@ -44,12 +44,14 @@ allprojects {
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
namespace 'org.apache.cordova'
|
||||
|
||||
compileSdkVersion cordovaConfig.COMPILE_SDK_VERSION
|
||||
buildToolsVersion cordovaConfig.BUILD_TOOLS_VERSION
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaLanguageVersion.of(cordovaConfig.JAVA_SOURCE_COMPATIBILITY)
|
||||
targetCompatibility JavaLanguageVersion.of(cordovaConfig.JAVA_TARGET_COMPATIBILITY)
|
||||
}
|
||||
|
||||
// For the Android Cordova Lib, we allow changing the minSdkVersion, but it is at the users own risk
|
||||
@@ -75,10 +77,16 @@ android {
|
||||
exclude 'META-INF/DEPENDENCIES'
|
||||
exclude 'META-INF/NOTICE'
|
||||
}
|
||||
|
||||
publishing {
|
||||
singleVariant('release') {
|
||||
withSourcesJar()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "androidx.appcompat:appcompat:${cordovaConfig.ANDROIDX_APP_COMPAT_VERSION}"
|
||||
api "androidx.appcompat:appcompat:${cordovaConfig.ANDROIDX_APP_COMPAT_VERSION}"
|
||||
implementation "androidx.webkit:webkit:${cordovaConfig.ANDROIDX_WEBKIT_VERSION}"
|
||||
implementation "androidx.core:core-splashscreen:${cordovaConfig.ANDROIDX_CORE_SPLASHSCREEN_VERSION}"
|
||||
}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
{
|
||||
"MIN_SDK_VERSION": 22,
|
||||
"SDK_VERSION": 32,
|
||||
"MIN_SDK_VERSION": 24,
|
||||
"SDK_VERSION": 34,
|
||||
"COMPILE_SDK_VERSION": null,
|
||||
"GRADLE_VERSION": "7.4.2",
|
||||
"MIN_BUILD_TOOLS_VERSION": "32.0.0",
|
||||
"AGP_VERSION": "7.2.1",
|
||||
"KOTLIN_VERSION": "1.5.21",
|
||||
"ANDROIDX_APP_COMPAT_VERSION": "1.4.2",
|
||||
"ANDROIDX_WEBKIT_VERSION": "1.4.0",
|
||||
"ANDROIDX_CORE_SPLASHSCREEN_VERSION": "1.0.0-rc01",
|
||||
"GRADLE_PLUGIN_GOOGLE_SERVICES_VERSION": "4.3.10",
|
||||
"GRADLE_VERSION": "8.7",
|
||||
"MIN_BUILD_TOOLS_VERSION": "34.0.0",
|
||||
"AGP_VERSION": "8.3.0",
|
||||
"KOTLIN_VERSION": "1.9.24",
|
||||
"ANDROIDX_APP_COMPAT_VERSION": "1.6.1",
|
||||
"ANDROIDX_WEBKIT_VERSION": "1.6.0",
|
||||
"ANDROIDX_CORE_SPLASHSCREEN_VERSION": "1.0.0",
|
||||
"GRADLE_PLUGIN_GOOGLE_SERVICES_VERSION": "4.3.15",
|
||||
"IS_GRADLE_PLUGIN_GOOGLE_SERVICES_ENABLED": false,
|
||||
"IS_GRADLE_PLUGIN_KOTLIN_ENABLED": false
|
||||
"IS_GRADLE_PLUGIN_KOTLIN_ENABLED": false,
|
||||
"PACKAGE_NAMESPACE": "io.cordova.helloCordova",
|
||||
"JAVA_SOURCE_COMPATIBILITY": 8,
|
||||
"JAVA_TARGET_COMPATIBILITY": 8,
|
||||
"KOTLIN_JVM_TARGET": null
|
||||
}
|
||||
|
||||
@@ -46,73 +46,69 @@ if (project.hasProperty('signEnabled')) {
|
||||
}
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar) {
|
||||
from android.sourceSets.main.java.srcDirs
|
||||
classifier = 'sources'
|
||||
}
|
||||
afterEvaluate {
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
groupId = 'org.apache.cordova'
|
||||
artifactId = 'framework'
|
||||
version = getCordovaAndroidVersion()
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
groupId = 'org.apache.cordova'
|
||||
artifactId = 'framework'
|
||||
version = getCordovaAndroidVersion()
|
||||
from components.release
|
||||
|
||||
artifact(sourcesJar)
|
||||
artifact("$buildDir/outputs/aar/framework-release.aar")
|
||||
pom {
|
||||
name = 'Cordova'
|
||||
description = 'A library to build Cordova-based projects for the Android platform.'
|
||||
url = 'https://cordova.apache.org'
|
||||
|
||||
pom {
|
||||
name = 'Cordova'
|
||||
description = 'A library to build Cordova-based projects for the Android platform.'
|
||||
url = 'https://cordova.apache.org'
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name = 'Apache License, Version 2.0'
|
||||
url = 'https://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||
licenses {
|
||||
license {
|
||||
name = 'Apache License, Version 2.0'
|
||||
url = 'https://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
id = 'stevengill'
|
||||
name = 'Steve Gill'
|
||||
developers {
|
||||
developer {
|
||||
id = 'stevengill'
|
||||
name = 'Steve Gill'
|
||||
}
|
||||
developer {
|
||||
id = 'erisu'
|
||||
name = 'Bryan Ellis'
|
||||
email = 'erisu@apache.org'
|
||||
}
|
||||
}
|
||||
developer {
|
||||
id = 'erisu'
|
||||
name = 'Bryan Ellis'
|
||||
email = 'erisu@apache.org'
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
connection = 'scm:git:https://github.com/apache/cordova-android.git'
|
||||
developerConnection = 'scm:git:git@github.com:apache/cordova-android.git'
|
||||
url = 'https://github.com/apache/cordova-android'
|
||||
scm {
|
||||
connection = 'scm:git:https://github.com/apache/cordova-android.git'
|
||||
developerConnection = 'scm:git:git@github.com:apache/cordova-android.git'
|
||||
url = 'https://github.com/apache/cordova-android'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
def releasesRepoUrl = 'https://repository.apache.org/content/repositories/releases'
|
||||
def snapshotsRepoUrl = 'https://repository.apache.org/content/repositories/snapshots'
|
||||
repositories {
|
||||
maven {
|
||||
def releasesRepoUrl = 'https://repository.apache.org/content/repositories/releases'
|
||||
def snapshotsRepoUrl = 'https://repository.apache.org/content/repositories/snapshots'
|
||||
|
||||
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
|
||||
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
|
||||
|
||||
credentials {
|
||||
if (project.hasProperty('apacheUsername') && project.hasProperty('apachePassword')) {
|
||||
username apacheUsername
|
||||
password apachePassword
|
||||
credentials {
|
||||
if (project.hasProperty('apacheUsername') && project.hasProperty('apachePassword')) {
|
||||
username apacheUsername
|
||||
password apachePassword
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signing {
|
||||
if (Boolean.valueOf(cdvEnableSigning)) {
|
||||
sign publishing.publications.mavenJava
|
||||
signing {
|
||||
if (Boolean.valueOf(cdvEnableSigning)) {
|
||||
sign publishing.publications.mavenJava
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,10 @@ Boolean isVersionValid(version) {
|
||||
return !(new Version(version)).isEqual('0.0.0')
|
||||
}
|
||||
|
||||
Boolean isVersionGreaterThanEqual(versionX, versionY) {
|
||||
return (new Version(versionX)) >= (new Version(versionY))
|
||||
}
|
||||
|
||||
String doFindLatestInstalledBuildTools(String minBuildToolsVersionString) {
|
||||
def buildToolsDirContents
|
||||
try {
|
||||
@@ -83,9 +87,9 @@ String doFindLatestInstalledBuildTools(String minBuildToolsVersionString) {
|
||||
String getAndroidSdkDir() {
|
||||
def rootDir = project.rootDir
|
||||
def androidSdkDir = null
|
||||
String envVar = System.getenv("ANDROID_SDK_ROOT")
|
||||
String envVar = System.getenv("ANDROID_HOME")
|
||||
if (envVar == null) {
|
||||
envVar = System.getenv("ANDROID_HOME")
|
||||
envVar = System.getenv("ANDROID_SDK_ROOT")
|
||||
}
|
||||
|
||||
def localProperties = new File(rootDir, 'local.properties')
|
||||
@@ -125,14 +129,6 @@ def doExtractIntFromManifest(name) {
|
||||
return new BigInteger(matcher.group(1))
|
||||
}
|
||||
|
||||
def doExtractStringFromManifest(name) {
|
||||
def manifestFile = file(android.sourceSets.main.manifest.srcFile)
|
||||
def pattern = Pattern.compile(name + "=\"(\\S+)\"")
|
||||
def matcher = pattern.matcher(manifestFile.getText())
|
||||
matcher.find()
|
||||
return matcher.group(1)
|
||||
}
|
||||
|
||||
def doGetConfigXml() {
|
||||
def xml = file("src/main/res/xml/config.xml").getText()
|
||||
// Disable namespace awareness since Cordova doesn't use them properly
|
||||
@@ -231,7 +227,6 @@ ext {
|
||||
privateHelpers.getProjectTarget = { doGetProjectTarget() }
|
||||
privateHelpers.applyCordovaConfigCustomization = { doApplyCordovaConfigCustomization() }
|
||||
privateHelpers.extractIntFromManifest = { name -> doExtractIntFromManifest(name) }
|
||||
privateHelpers.extractStringFromManifest = { name -> doExtractStringFromManifest(name) }
|
||||
privateHelpers.ensureValueExists = { filePath, props, key -> doEnsureValueExists(filePath, props, key) }
|
||||
|
||||
// These helpers can be used by plugins / projects and will not change.
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
# 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.
|
||||
|
||||
# Project-wide Gradle settings.
|
||||
|
||||
# IDE (e.g. Android Studio) users:
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
# 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.
|
||||
|
||||
#Thu Nov 09 10:50:25 PST 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
# 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.
|
||||
|
||||
# GENERATED FILE! DO NOT EDIT!
|
||||
|
||||
# This file was originally created by the Android Tools, but is now
|
||||
|
||||
@@ -51,7 +51,8 @@ public class BuildHelper {
|
||||
{
|
||||
try
|
||||
{
|
||||
Class<?> clazz = Class.forName(ctx.getClass().getPackage().getName() + ".BuildConfig");
|
||||
String packageName = ctx.getApplicationInfo().packageName;
|
||||
Class<?> clazz = Class.forName(packageName + ".BuildConfig");
|
||||
Field field = clazz.getField(key);
|
||||
return field.get(null);
|
||||
} catch (ClassNotFoundException e) {
|
||||
|
||||
@@ -34,6 +34,7 @@ public class ConfigXmlParser {
|
||||
private static String SCHEME_HTTP = "http";
|
||||
private static String SCHEME_HTTPS = "https";
|
||||
private static String DEFAULT_HOSTNAME = "localhost";
|
||||
private static final String DEFAULT_CONTENT_SRC = "index.html";
|
||||
|
||||
private String launchUrl;
|
||||
private String contentSrc;
|
||||
@@ -110,6 +111,18 @@ public class ConfigXmlParser {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
onPostParse();
|
||||
}
|
||||
|
||||
private void onPostParse() {
|
||||
// After parsing, if contentSrc is still null, it signals
|
||||
// that <content> tag was completely missing. In this case,
|
||||
// default it.
|
||||
// https://github.com/apache/cordova-android/issues/1432
|
||||
if (contentSrc == null) {
|
||||
contentSrc = DEFAULT_CONTENT_SRC;
|
||||
}
|
||||
}
|
||||
|
||||
public void handleStartTag(XmlPullParser xml) {
|
||||
@@ -140,7 +153,7 @@ public class ConfigXmlParser {
|
||||
contentSrc = src;
|
||||
} else {
|
||||
// Default
|
||||
contentSrc = "index.html";
|
||||
contentSrc = DEFAULT_CONTENT_SRC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,6 +391,7 @@ public class CordovaActivity extends AppCompatActivity {
|
||||
if ((errorUrl != null) && (!failingUrl.equals(errorUrl)) && (appView != null)) {
|
||||
// Load URL on UI thread
|
||||
me.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
me.appView.showWebPage(errorUrl, false, true, null);
|
||||
}
|
||||
@@ -400,6 +401,7 @@ public class CordovaActivity extends AppCompatActivity {
|
||||
else {
|
||||
final boolean exit = !(errorCode == WebViewClient.ERROR_HOST_LOOKUP);
|
||||
me.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (exit) {
|
||||
me.appView.getView().setVisibility(View.GONE);
|
||||
@@ -416,6 +418,7 @@ public class CordovaActivity extends AppCompatActivity {
|
||||
public void displayError(final String title, final String message, final String button, final boolean exit) {
|
||||
final CordovaActivity me = this;
|
||||
me.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(me);
|
||||
@@ -424,6 +427,7 @@ public class CordovaActivity extends AppCompatActivity {
|
||||
dlg.setCancelable(false);
|
||||
dlg.setPositiveButton(button,
|
||||
new AlertDialog.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
if (exit) {
|
||||
@@ -488,6 +492,7 @@ public class CordovaActivity extends AppCompatActivity {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
cordovaInterface.onSaveInstanceState(outState);
|
||||
super.onSaveInstanceState(outState);
|
||||
|
||||
@@ -41,6 +41,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
|
||||
* Cancel this request
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public void cancel()
|
||||
{
|
||||
request.cancel();
|
||||
@@ -50,6 +51,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
|
||||
* Returns the host name of the server requesting the certificate.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public String getHost()
|
||||
{
|
||||
return request.getHost();
|
||||
@@ -59,6 +61,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
|
||||
* Returns the acceptable types of asymmetric keys (can be null).
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public String[] getKeyTypes()
|
||||
{
|
||||
return request.getKeyTypes();
|
||||
@@ -68,6 +71,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
|
||||
* Returns the port number of the server requesting the certificate.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public int getPort()
|
||||
{
|
||||
return request.getPort();
|
||||
@@ -77,6 +81,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
|
||||
* Returns the acceptable certificate issuers for the certificate matching the private key (can be null).
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public Principal[] getPrincipals()
|
||||
{
|
||||
return request.getPrincipals();
|
||||
@@ -86,6 +91,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
|
||||
* Ignore the request for now. Do not remember user's choice.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public void ignore()
|
||||
{
|
||||
request.ignore();
|
||||
@@ -98,6 +104,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
|
||||
* @param chain The certificate chain
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public void proceed(PrivateKey privateKey, X509Certificate[] chain)
|
||||
{
|
||||
request.proceed(privateKey, chain);
|
||||
|
||||
@@ -43,18 +43,21 @@ public class CordovaDialogsHelper {
|
||||
dlg.setCancelable(true);
|
||||
dlg.setPositiveButton(android.R.string.ok,
|
||||
new AlertDialog.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
result.gotResult(true, null);
|
||||
}
|
||||
});
|
||||
dlg.setOnCancelListener(
|
||||
new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
result.gotResult(false, null);
|
||||
}
|
||||
});
|
||||
dlg.setOnKeyListener(new DialogInterface.OnKeyListener() {
|
||||
//DO NOTHING
|
||||
@Override
|
||||
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK)
|
||||
{
|
||||
@@ -75,24 +78,28 @@ public class CordovaDialogsHelper {
|
||||
dlg.setCancelable(true);
|
||||
dlg.setPositiveButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
result.gotResult(true, null);
|
||||
}
|
||||
});
|
||||
dlg.setNegativeButton(android.R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
result.gotResult(false, null);
|
||||
}
|
||||
});
|
||||
dlg.setOnCancelListener(
|
||||
new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
result.gotResult(false, null);
|
||||
}
|
||||
});
|
||||
dlg.setOnKeyListener(new DialogInterface.OnKeyListener() {
|
||||
//DO NOTHING
|
||||
@Override
|
||||
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK)
|
||||
{
|
||||
@@ -126,6 +133,7 @@ public class CordovaDialogsHelper {
|
||||
dlg.setCancelable(false);
|
||||
dlg.setPositiveButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
String userText = input.getText().toString();
|
||||
result.gotResult(true, userText);
|
||||
@@ -133,6 +141,7 @@ public class CordovaDialogsHelper {
|
||||
});
|
||||
dlg.setNegativeButton(android.R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
result.gotResult(false, null);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ public class CordovaHttpAuthHandler implements ICordovaHttpAuthHandler {
|
||||
/**
|
||||
* Instructs the WebView to cancel the authentication request.
|
||||
*/
|
||||
@Override
|
||||
public void cancel () {
|
||||
this.handler.cancel();
|
||||
}
|
||||
@@ -45,6 +46,7 @@ public class CordovaHttpAuthHandler implements ICordovaHttpAuthHandler {
|
||||
* @param username
|
||||
* @param password
|
||||
*/
|
||||
@Override
|
||||
public void proceed (String username, String password) {
|
||||
this.handler.proceed(username, password);
|
||||
}
|
||||
|
||||
@@ -223,28 +223,23 @@ public class CordovaInterfaceImpl implements CordovaInterface {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestPermission(CordovaPlugin plugin, int requestCode, String permission) {
|
||||
String[] permissions = new String [1];
|
||||
permissions[0] = permission;
|
||||
requestPermissions(plugin, requestCode, permissions);
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public void requestPermissions(CordovaPlugin plugin, int requestCode, String [] permissions) {
|
||||
int mappedRequestCode = permissionResultCallbacks.registerCallback(plugin, requestCode);
|
||||
getActivity().requestPermissions(permissions, mappedRequestCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission)
|
||||
{
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||
{
|
||||
int result = activity.checkSelfPermission(permission);
|
||||
return PackageManager.PERMISSION_GRANTED == result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return PackageManager.PERMISSION_GRANTED == activity.checkSelfPermission(permission);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ import android.content.res.Configuration;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.webkit.RenderProcessGoneDetail;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
@@ -442,4 +444,19 @@ public class CordovaPlugin {
|
||||
public CordovaPluginPathHandler getPathHandler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the WebView's render process has exited. Can be used to collect information regarding the crash for crashlytics or optionally attempt to gracefully handle/recover the crashed webview by recreating it.
|
||||
*
|
||||
* See <a href="https://developer.android.com/reference/android/webkit/WebViewClient#onRenderProcessGone(android.webkit.WebView,%20android.webkit.RenderProcessGoneDetail)">WebViewClient#onRenderProcessGone</a>
|
||||
*
|
||||
* Note: A plugin must not attempt to recover a webview that it does not own/manage.
|
||||
*
|
||||
* @return true if the host application handled the situation that process has exited,
|
||||
* otherwise, application will crash if render process crashed, or be killed
|
||||
* if render process was killed by the system.
|
||||
*/
|
||||
public boolean onRenderProcessGone(final WebView view, RenderProcessGoneDetail detail) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import android.webkit.WebChromeClient.CustomViewCallback;
|
||||
* are not expected to implement it.
|
||||
*/
|
||||
public interface CordovaWebView {
|
||||
public static final String CORDOVA_VERSION = "11.0.0";
|
||||
public static final String CORDOVA_VERSION = "13.0.0";
|
||||
|
||||
void init(CordovaInterface cordova, List<PluginEntry> pluginEntries, CordovaPreferences preferences);
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ public class CordovaWebViewImpl implements CordovaWebView {
|
||||
// This isn't enforced by the compiler, so assert here.
|
||||
assert engine.getView() instanceof CordovaWebViewEngine.EngineView;
|
||||
|
||||
pluginManager.addService(CoreAndroid.PLUGIN_NAME, "org.apache.cordova.CoreAndroid");
|
||||
pluginManager.addService(CoreAndroid.PLUGIN_NAME, "org.apache.cordova.CoreAndroid", true);
|
||||
pluginManager.init();
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@ public class CordovaWebViewImpl implements CordovaWebView {
|
||||
|
||||
// Timeout error method
|
||||
final Runnable loadError = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
stopLoading();
|
||||
LOG.e(TAG, "CordovaWebView: TIMEOUT ERROR!");
|
||||
@@ -168,6 +169,7 @@ public class CordovaWebViewImpl implements CordovaWebView {
|
||||
|
||||
// Timeout timer method
|
||||
final Runnable timeoutCheck = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
synchronized (this) {
|
||||
@@ -189,6 +191,7 @@ public class CordovaWebViewImpl implements CordovaWebView {
|
||||
if (cordova.getActivity() != null) {
|
||||
final boolean _recreatePlugins = recreatePlugins;
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (loadUrlTimeoutValue > 0) {
|
||||
cordova.getThreadPool().execute(timeoutCheck);
|
||||
@@ -579,11 +582,13 @@ public class CordovaWebViewImpl implements CordovaWebView {
|
||||
// Make app visible after 2 sec in case there was a JS error and Cordova JS never initialized correctly
|
||||
if (engine.getView().getVisibility() != View.VISIBLE) {
|
||||
Thread t = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
if (cordova.getActivity() != null) {
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
pluginManager.postMessage("spinner", "stop");
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.BuildHelper;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@@ -30,7 +32,6 @@ import android.content.IntentFilter;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
@@ -72,6 +73,7 @@ public class CoreAndroid extends CordovaPlugin {
|
||||
* @param callbackContext The callback context from which we were invoked.
|
||||
* @return A PluginResult object with a status and message.
|
||||
*/
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
@@ -85,6 +87,7 @@ public class CoreAndroid extends CordovaPlugin {
|
||||
// I recommend we change the name of the Message as spinner/stop is not
|
||||
// indicative of what this actually does (shows the webview).
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
webView.getPluginManager().postMessage("spinner", "stop");
|
||||
}
|
||||
@@ -143,6 +146,7 @@ public class CoreAndroid extends CordovaPlugin {
|
||||
*/
|
||||
public void clearCache() {
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
webView.clearCache();
|
||||
}
|
||||
@@ -214,6 +218,7 @@ public class CoreAndroid extends CordovaPlugin {
|
||||
*/
|
||||
public void clearHistory() {
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
webView.clearHistory();
|
||||
}
|
||||
@@ -226,6 +231,7 @@ public class CoreAndroid extends CordovaPlugin {
|
||||
*/
|
||||
public void backHistory() {
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
webView.backHistory();
|
||||
}
|
||||
@@ -352,6 +358,7 @@ public class CoreAndroid extends CordovaPlugin {
|
||||
* Unregister the receiver
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void onDestroy()
|
||||
{
|
||||
webView.getContext().unregisterReceiver(this.telephonyReceiver);
|
||||
@@ -376,35 +383,19 @@ public class CoreAndroid extends CordovaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* This needs to be implemented if you wish to use the Camera Plugin or other plugins
|
||||
* that read the Build Configuration.
|
||||
*
|
||||
* Thanks to Phil@Medtronic and Graham Borland for finding the answer and posting it to
|
||||
* StackOverflow. This is annoying as hell!
|
||||
*
|
||||
* @deprecated Use {@link BuildHelper#getBuildConfigValue} instead.
|
||||
*/
|
||||
|
||||
@Deprecated
|
||||
public static Object getBuildConfigValue(Context ctx, String key)
|
||||
{
|
||||
try
|
||||
{
|
||||
Class<?> clazz = Class.forName(ctx.getClass().getPackage().getName() + ".BuildConfig");
|
||||
Field field = clazz.getField(key);
|
||||
return field.get(null);
|
||||
} catch (ClassNotFoundException e) {
|
||||
LOG.d(TAG, "Unable to get the BuildConfig, is this built with ANT?");
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchFieldException e) {
|
||||
LOG.d(TAG, key + " is not a valid field. Check your build.gradle");
|
||||
} catch (IllegalAccessException e) {
|
||||
LOG.d(TAG, "Illegal Access Exception: Let's print a stack trace.");
|
||||
e.printStackTrace();
|
||||
} catch (NullPointerException e) {
|
||||
LOG.d(TAG, "Null Pointer Exception: Let's print a stack trace.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
LOG.w(TAG, "CoreAndroid.getBuildConfigValue is deprecated and will be removed in a future release. Use BuildHelper.getBuildConfigValue instead.");
|
||||
return BuildHelper.getBuildConfigValue(ctx, key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,6 +302,7 @@ public class NativeToJsMessageQueue {
|
||||
@Override
|
||||
public void onNativeToJsMessageAvailable(final NativeToJsMessageQueue queue) {
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String js = queue.popAndEncodeAsJs();
|
||||
if (js != null) {
|
||||
@@ -330,6 +331,7 @@ public class NativeToJsMessageQueue {
|
||||
@Override
|
||||
public void reset() {
|
||||
delegate.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
online = false;
|
||||
// If the following call triggers a notifyOfFlush, then ignore it.
|
||||
@@ -342,6 +344,7 @@ public class NativeToJsMessageQueue {
|
||||
@Override
|
||||
public void onNativeToJsMessageAvailable(final NativeToJsMessageQueue queue) {
|
||||
delegate.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!queue.isEmpty()) {
|
||||
ignoreNextFlush = false;
|
||||
@@ -372,6 +375,7 @@ public class NativeToJsMessageQueue {
|
||||
@Override
|
||||
public void onNativeToJsMessageAvailable(final NativeToJsMessageQueue queue) {
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String js = queue.popAndEncodeAsJs();
|
||||
if (js != null) {
|
||||
|
||||
@@ -32,6 +32,8 @@ import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Debug;
|
||||
import android.os.Build;
|
||||
import android.webkit.RenderProcessGoneDetail;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
* PluginManager is exposed to JavaScript in the Cordova WebView.
|
||||
@@ -197,7 +199,18 @@ public class PluginManager {
|
||||
* @param className The plugin class name
|
||||
*/
|
||||
public void addService(String service, String className) {
|
||||
PluginEntry entry = new PluginEntry(service, className, false);
|
||||
addService(service, className, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a plugin class that implements a service to the service entry table.
|
||||
*
|
||||
* @param service The service name
|
||||
* @param className The plugin class name
|
||||
* @param onload If true, the plugin will be instantiated immediately
|
||||
*/
|
||||
public void addService(String service, String className, boolean onload) {
|
||||
PluginEntry entry = new PluginEntry(service, className, onload);
|
||||
this.addService(entry);
|
||||
}
|
||||
|
||||
@@ -339,22 +352,11 @@ public class PluginManager {
|
||||
public Object postMessage(String id, Object data) {
|
||||
LOG.d(TAG, "postMessage: " + id);
|
||||
synchronized (this.pluginMap) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
this.pluginMap.forEach((s, plugin) -> {
|
||||
if (plugin != null) {
|
||||
plugin.onMessage(id, data);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
for (CordovaPlugin plugin : this.pluginMap.values()) {
|
||||
if (plugin != null) {
|
||||
Object obj = plugin.onMessage(id, data);
|
||||
if (obj != null) {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
this.pluginMap.forEach((s, plugin) -> {
|
||||
if (plugin != null) {
|
||||
plugin.onMessage(id, data);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return ctx.onMessage(id, data);
|
||||
}
|
||||
@@ -617,4 +619,29 @@ public class PluginManager {
|
||||
}
|
||||
return handlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the WebView's render process has exited.
|
||||
*
|
||||
* See https://developer.android.com/reference/android/webkit/WebViewClient#onRenderProcessGone(android.webkit.WebView,%20android.webkit.RenderProcessGoneDetail)
|
||||
*
|
||||
* @return true if the host application handled the situation that process has exited,
|
||||
* otherwise, application will crash if render process crashed, or be killed
|
||||
* if render process was killed by the system.
|
||||
*/
|
||||
public boolean onRenderProcessGone(final WebView view, RenderProcessGoneDetail detail) {
|
||||
boolean result = false;
|
||||
synchronized (this.entryMap) {
|
||||
for (PluginEntry entry : this.entryMap.values()) {
|
||||
CordovaPlugin plugin = pluginMap.get(entry.service);
|
||||
if (plugin != null) {
|
||||
if (plugin.onRenderProcessGone(view, detail)) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ public class SplashScreenPlugin extends CordovaPlugin {
|
||||
|
||||
// auto hide splash screen when custom delay is defined.
|
||||
if (autoHide && delayTime != DEFAULT_DELAY_TIME) {
|
||||
Handler splashScreenDelayHandler = new Handler();
|
||||
Handler splashScreenDelayHandler = new Handler(cordova.getContext().getMainLooper());
|
||||
splashScreenDelayHandler.postDelayed(() -> keepOnScreen = false, delayTime);
|
||||
}
|
||||
|
||||
@@ -137,27 +137,29 @@ public class SplashScreenPlugin extends CordovaPlugin {
|
||||
// If auto hide is disabled (false), the hiding of the splash screen must be determined &
|
||||
// triggered by the front-end code with the `navigator.splashscreen.hide()` method.
|
||||
|
||||
// Setup the fade
|
||||
splashScreen.setOnExitAnimationListener(new SplashScreen.OnExitAnimationListener() {
|
||||
@Override
|
||||
public void onSplashScreenExit(@NonNull SplashScreenViewProvider splashScreenViewProvider) {
|
||||
View splashScreenView = splashScreenViewProvider.getView();
|
||||
if (isFadeEnabled) {
|
||||
// Setup the fade
|
||||
splashScreen.setOnExitAnimationListener(new SplashScreen.OnExitAnimationListener() {
|
||||
@Override
|
||||
public void onSplashScreenExit(@NonNull SplashScreenViewProvider splashScreenViewProvider) {
|
||||
View splashScreenView = splashScreenViewProvider.getView();
|
||||
|
||||
splashScreenView
|
||||
.animate()
|
||||
.alpha(0.0f)
|
||||
.setDuration(isFadeEnabled ? fadeDuration : 0)
|
||||
.setStartDelay(isFadeEnabled ? 0 : fadeDuration)
|
||||
.setInterpolator(new AccelerateInterpolator())
|
||||
.setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
splashScreenViewProvider.remove();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
});
|
||||
splashScreenView
|
||||
.animate()
|
||||
.alpha(0.0f)
|
||||
.setDuration(fadeDuration)
|
||||
.setStartDelay(0)
|
||||
.setInterpolator(new AccelerateInterpolator())
|
||||
.setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
splashScreenViewProvider.remove();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void attemptCloseOnPageFinished() {
|
||||
|
||||
@@ -41,22 +41,27 @@ class SystemCookieManager implements ICordovaCookieManager {
|
||||
cookieManager.setAcceptFileSchemeCookies(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCookiesEnabled(boolean accept) {
|
||||
cookieManager.setAcceptCookie(accept);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCookie(final String url, final String value) {
|
||||
cookieManager.setCookie(url, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCookie(final String url) {
|
||||
return cookieManager.getCookie(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCookies() {
|
||||
cookieManager.removeAllCookies(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
cookieManager.flush();
|
||||
}
|
||||
|
||||
@@ -37,16 +37,19 @@ class SystemExposedJsApi implements ExposedJsApi {
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
@Override
|
||||
public String exec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException {
|
||||
return bridge.jsExec(bridgeSecret, service, action, callbackId, arguments);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
@Override
|
||||
public void setNativeToJsBridgeMode(int bridgeSecret, int value) throws IllegalAccessException {
|
||||
bridge.jsSetNativeToJsBridgeMode(bridgeSecret, value);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
@Override
|
||||
public String retrieveJsMessages(int bridgeSecret, boolean fromOnlineEvent) throws IllegalAccessException {
|
||||
return bridge.jsRetrieveJsMessages(bridgeSecret, fromOnlineEvent);
|
||||
}
|
||||
|
||||
@@ -18,18 +18,22 @@
|
||||
*/
|
||||
package org.apache.cordova.engine;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import android.annotation.TargetApi;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import android.app.Activity;
|
||||
import android.content.ClipData;
|
||||
import android.content.Context;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.MediaStore;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.webkit.ConsoleMessage;
|
||||
import android.webkit.GeolocationPermissions.Callback;
|
||||
import android.webkit.JsPromptResult;
|
||||
import android.webkit.JsResult;
|
||||
@@ -41,6 +45,7 @@ import android.webkit.PermissionRequest;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RelativeLayout;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import org.apache.cordova.CordovaDialogsHelper;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
@@ -212,52 +217,110 @@ public class SystemWebChromeClient extends WebChromeClient {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShowFileChooser(WebView webView, final ValueCallback<Uri[]> filePathsCallback, final WebChromeClient.FileChooserParams fileChooserParams) {
|
||||
public boolean onShowFileChooser(WebView webView, final ValueCallback<Uri[]> filePathsCallback,
|
||||
final WebChromeClient.FileChooserParams fileChooserParams) {
|
||||
Intent fileIntent = fileChooserParams.createIntent();
|
||||
|
||||
// Check if multiple-select is specified
|
||||
Boolean selectMultiple = false;
|
||||
if (fileChooserParams.getMode() == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE) {
|
||||
selectMultiple = true;
|
||||
}
|
||||
Intent intent = fileChooserParams.createIntent();
|
||||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, selectMultiple);
|
||||
|
||||
fileIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, selectMultiple);
|
||||
|
||||
// Uses Intent.EXTRA_MIME_TYPES to pass multiple mime types.
|
||||
String[] acceptTypes = fileChooserParams.getAcceptTypes();
|
||||
if (acceptTypes.length > 1) {
|
||||
intent.setType("*/*"); // Accept all, filter mime types by Intent.EXTRA_MIME_TYPES.
|
||||
intent.putExtra(Intent.EXTRA_MIME_TYPES, acceptTypes);
|
||||
fileIntent.setType("*/*"); // Accept all, filter mime types by Intent.EXTRA_MIME_TYPES.
|
||||
fileIntent.putExtra(Intent.EXTRA_MIME_TYPES, acceptTypes);
|
||||
}
|
||||
|
||||
// Image from camera intent
|
||||
Uri tempUri = null;
|
||||
Intent captureIntent = null;
|
||||
if (fileChooserParams.isCaptureEnabled()) {
|
||||
captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
Context context = parentEngine.getView().getContext();
|
||||
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)
|
||||
&& captureIntent.resolveActivity(context.getPackageManager()) != null) {
|
||||
try {
|
||||
File tempFile = createTempFile(context);
|
||||
LOG.d(LOG_TAG, "Temporary photo capture file: " + tempFile);
|
||||
tempUri = createUriForFile(context, tempFile);
|
||||
LOG.d(LOG_TAG, "Temporary photo capture URI: " + tempUri);
|
||||
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, tempUri);
|
||||
} catch (IOException e) {
|
||||
LOG.e(LOG_TAG, "Unable to create temporary file for photo capture", e);
|
||||
captureIntent = null;
|
||||
}
|
||||
} else {
|
||||
LOG.w(LOG_TAG, "Device does not support photo capture");
|
||||
captureIntent = null;
|
||||
}
|
||||
}
|
||||
final Uri captureUri = tempUri;
|
||||
|
||||
// Chooser intent
|
||||
Intent chooserIntent = Intent.createChooser(fileIntent, null);
|
||||
if (captureIntent != null) {
|
||||
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { captureIntent });
|
||||
}
|
||||
|
||||
try {
|
||||
LOG.i(LOG_TAG, "Starting intent for file chooser");
|
||||
parentEngine.cordova.startActivityForResult(new CordovaPlugin() {
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
// Handle result
|
||||
Uri[] result = null;
|
||||
if (resultCode == Activity.RESULT_OK && intent != null) {
|
||||
if (intent.getClipData() != null) {
|
||||
// handle multiple-selected files
|
||||
final int numSelectedFiles = intent.getClipData().getItemCount();
|
||||
result = new Uri[numSelectedFiles];
|
||||
for (int i = 0; i < numSelectedFiles; i++) {
|
||||
result[i] = intent.getClipData().getItemAt(i).getUri();
|
||||
LOG.d(LOG_TAG, "Receive file chooser URL: " + result[i]);
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
List<Uri> uris = new ArrayList<Uri>();
|
||||
|
||||
if (intent != null && intent.getData() != null) { // single file
|
||||
LOG.v(LOG_TAG, "Adding file (single): " + intent.getData());
|
||||
uris.add(intent.getData());
|
||||
} else if (captureUri != null) { // camera
|
||||
LOG.v(LOG_TAG, "Adding camera capture: " + captureUri);
|
||||
uris.add(captureUri);
|
||||
} else if (intent != null && intent.getClipData() != null) { // multiple files
|
||||
ClipData clipData = intent.getClipData();
|
||||
int count = clipData.getItemCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
Uri uri = clipData.getItemAt(i).getUri();
|
||||
LOG.v(LOG_TAG, "Adding file (multiple): " + uri);
|
||||
if (uri != null) {
|
||||
uris.add(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (intent.getData() != null) {
|
||||
// handle single-selected file
|
||||
result = WebChromeClient.FileChooserParams.parseResult(resultCode, intent);
|
||||
LOG.d(LOG_TAG, "Receive file chooser URL: " + result);
|
||||
|
||||
if (!uris.isEmpty()) {
|
||||
LOG.d(LOG_TAG, "Receive file chooser URL: " + uris.toString());
|
||||
result = uris.toArray(new Uri[uris.size()]);
|
||||
}
|
||||
}
|
||||
filePathsCallback.onReceiveValue(result);
|
||||
}
|
||||
}, intent, FILECHOOSER_RESULTCODE);
|
||||
}, chooserIntent, FILECHOOSER_RESULTCODE);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
LOG.w("No activity found to handle file chooser intent.", e);
|
||||
LOG.w(LOG_TAG, "No activity found to handle file chooser intent.", e);
|
||||
filePathsCallback.onReceiveValue(null);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private File createTempFile(Context context) throws IOException {
|
||||
// Create an image file name
|
||||
File tempFile = File.createTempFile("temp", ".jpg", context.getCacheDir());
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
private Uri createUriForFile(Context context, File tempFile) throws IOException {
|
||||
String appId = context.getPackageName();
|
||||
Uri uri = FileProvider.getUriForFile(context, appId + ".cdv.core.file.provider", tempFile);
|
||||
return uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionRequest(final PermissionRequest request) {
|
||||
LOG.d(LOG_TAG, "onPermissionRequest: " + Arrays.toString(request.getResources()));
|
||||
|
||||
@@ -28,6 +28,9 @@ import android.net.http.SslError;
|
||||
import android.webkit.ClientCertRequest;
|
||||
import android.webkit.HttpAuthHandler;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.webkit.RenderProcessGoneDetail;
|
||||
import android.webkit.ServiceWorkerClient;
|
||||
import android.webkit.ServiceWorkerController;
|
||||
import android.webkit.SslErrorHandler;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebResourceResponse;
|
||||
@@ -115,6 +118,18 @@ public class SystemWebViewClient extends WebViewClient {
|
||||
});
|
||||
|
||||
this.assetLoader = assetLoaderBuilder.build();
|
||||
boolean setAsServiceWorkerClient = parentEngine.preferences.getBoolean("ResolveServiceWorkerRequests", true);
|
||||
ServiceWorkerController controller = null;
|
||||
|
||||
if (setAsServiceWorkerClient) {
|
||||
controller = ServiceWorkerController.getInstance();
|
||||
controller.setServiceWorkerClient(new ServiceWorkerClient(){
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebResourceRequest request) {
|
||||
return assetLoader.shouldInterceptRequest(request.getUrl());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -422,4 +437,15 @@ public class SystemWebViewClient extends WebViewClient {
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
|
||||
return this.assetLoader.shouldInterceptRequest(request.getUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRenderProcessGone(final WebView view, RenderProcessGoneDetail detail) {
|
||||
// Check if there is some plugin which can handle this event
|
||||
PluginManager pluginManager = this.parentEngine.pluginManager;
|
||||
if (pluginManager != null && pluginManager.onRenderProcessGone(view, detail)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onRenderProcessGone(view, detail);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,9 +175,20 @@ public class SystemWebViewEngine implements CordovaWebViewEngine {
|
||||
String databasePath = webView.getContext().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
|
||||
settings.setDatabaseEnabled(true);
|
||||
|
||||
//Determine whether we're in debug or release mode, and turn on Debugging!
|
||||
ApplicationInfo appInfo = webView.getContext().getApplicationContext().getApplicationInfo();
|
||||
if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
|
||||
// The default is to use the module's debuggable state to decide if the webview inspecter
|
||||
// should be enabled. However, users can configure InspectableWebview preference to forcefully enable
|
||||
// or disable the webview inspecter.
|
||||
String inspectableWebview = preferences.getString("InspectableWebview", null);
|
||||
boolean shouldEnableInspector = false;
|
||||
if (inspectableWebview == null) {
|
||||
ApplicationInfo appInfo = webView.getContext().getApplicationContext().getApplicationInfo();
|
||||
shouldEnableInspector = (appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
|
||||
}
|
||||
else if ("true".equals(inspectableWebview)) {
|
||||
shouldEnableInspector = true;
|
||||
}
|
||||
|
||||
if (shouldEnableInspector) {
|
||||
enableRemoteDebugging();
|
||||
}
|
||||
|
||||
|
||||
@@ -50,15 +50,6 @@ class AndroidManifest {
|
||||
return this;
|
||||
}
|
||||
|
||||
getPackageId () {
|
||||
return this.doc.getroot().attrib.package;
|
||||
}
|
||||
|
||||
setPackageId (pkgId) {
|
||||
this.doc.getroot().attrib.package = pkgId;
|
||||
return this;
|
||||
}
|
||||
|
||||
getActivity () {
|
||||
const activity = this.doc.getroot().find('./application/activity');
|
||||
return {
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const properties_parser = require('properties-parser');
|
||||
const AndroidManifest = require('./AndroidManifest');
|
||||
const pluginHandlers = require('./pluginHandlers');
|
||||
const CordovaGradleConfigParserFactory = require('./config/CordovaGradleConfigParserFactory');
|
||||
|
||||
let projectFileCache = {};
|
||||
|
||||
@@ -63,17 +63,17 @@ class AndroidProject {
|
||||
this.projectDir = projectDir;
|
||||
this.platformWww = path.join(this.projectDir, 'platform_www');
|
||||
this.www = path.join(this.projectDir, 'app/src/main/assets/www');
|
||||
this.cordovaGradleConfigParser = CordovaGradleConfigParserFactory.create(this.projectDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the package name out of the Android Manifest file
|
||||
* Reads the package name out of the Cordova's Gradle Config file
|
||||
*
|
||||
* @param {String} projectDir The absolute path to the directory containing the project
|
||||
* @return {String} The name of the package
|
||||
*/
|
||||
getPackageName () {
|
||||
const manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml');
|
||||
return new AndroidManifest(manifestPath).getPackageId();
|
||||
return this.cordovaGradleConfigParser.getPackageName();
|
||||
}
|
||||
|
||||
getCustomSubprojectRelativeDir (plugin_id, src) {
|
||||
|
||||
@@ -277,6 +277,12 @@ class Api {
|
||||
});
|
||||
}
|
||||
|
||||
listTargets (options) {
|
||||
return require('./check_reqs').check_android().then(() => {
|
||||
return require('./run').runListDevices.call(this, options);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans out the build artifacts from platform's directory, and also
|
||||
* cleans out the platform www directory if called without options specified.
|
||||
|
||||
10
lib/build.js
10
lib/build.js
@@ -21,6 +21,7 @@ const path = require('path');
|
||||
const fs = require('fs');
|
||||
const nopt = require('nopt');
|
||||
const untildify = require('untildify');
|
||||
const { parseArgsStringToArgv } = require('string-argv');
|
||||
|
||||
const Adb = require('./Adb');
|
||||
|
||||
@@ -36,7 +37,11 @@ function parseOpts (options, resolvedTarget, projectRoot) {
|
||||
minSdkVersion: String,
|
||||
maxSdkVersion: String,
|
||||
targetSdkVersion: String,
|
||||
|
||||
// This needs to be an array since nopts will parse its entries as further options for this process
|
||||
// It will be an array of 1 string: [ "string args" ]
|
||||
gradleArg: [String, Array],
|
||||
|
||||
keystore: path,
|
||||
alias: String,
|
||||
storePassword: String,
|
||||
@@ -58,7 +63,8 @@ function parseOpts (options, resolvedTarget, projectRoot) {
|
||||
if (options.argv.maxSdkVersion) { ret.extraArgs.push('-PcdvMaxSdkVersion=' + options.argv.maxSdkVersion); }
|
||||
if (options.argv.targetSdkVersion) { ret.extraArgs.push('-PcdvTargetSdkVersion=' + options.argv.targetSdkVersion); }
|
||||
if (options.argv.gradleArg) {
|
||||
ret.extraArgs = ret.extraArgs.concat(options.argv.gradleArg);
|
||||
const gradleArgs = parseArgsStringToArgv(options.argv.gradleArg[0]);
|
||||
ret.extraArgs = ret.extraArgs.concat(gradleArgs);
|
||||
}
|
||||
|
||||
const packageArgs = {};
|
||||
@@ -184,7 +190,7 @@ module.exports.run = function (options, optResolvedTarget) {
|
||||
}
|
||||
|
||||
return {
|
||||
paths: paths,
|
||||
paths,
|
||||
buildType: opts.buildType
|
||||
};
|
||||
});
|
||||
|
||||
@@ -25,8 +25,9 @@ const events = require('cordova-common').events;
|
||||
const CordovaError = require('cordova-common').CordovaError;
|
||||
const check_reqs = require('../check_reqs');
|
||||
const PackageType = require('../PackageType');
|
||||
const { compareByAll } = require('../utils');
|
||||
const { compareByAll, isWindows } = require('../utils');
|
||||
const { createEditor } = require('properties-parser');
|
||||
const CordovaGradleConfigParserFactory = require('../config/CordovaGradleConfigParserFactory');
|
||||
|
||||
const MARKER = 'YOUR CHANGES WILL BE ERASED!';
|
||||
const SIGNING_PROPERTIES = '-signing.properties';
|
||||
@@ -84,7 +85,11 @@ class ProjectBuilder {
|
||||
}
|
||||
|
||||
getArgs (cmd, opts) {
|
||||
let args;
|
||||
let args = [];
|
||||
if (opts.extraArgs) {
|
||||
args = args.concat(opts.extraArgs);
|
||||
}
|
||||
|
||||
let buildCmd = cmd;
|
||||
if (opts.packageType === PackageType.BUNDLE) {
|
||||
if (cmd === 'release') {
|
||||
@@ -92,8 +97,6 @@ class ProjectBuilder {
|
||||
} else if (cmd === 'debug') {
|
||||
buildCmd = ':app:bundleDebug';
|
||||
}
|
||||
|
||||
args = [buildCmd, '-b', path.join(this.root, 'build.gradle')];
|
||||
} else {
|
||||
if (cmd === 'release') {
|
||||
buildCmd = 'cdvBuildRelease';
|
||||
@@ -101,28 +104,37 @@ class ProjectBuilder {
|
||||
buildCmd = 'cdvBuildDebug';
|
||||
}
|
||||
|
||||
args = [buildCmd, '-b', path.join(this.root, 'build.gradle')];
|
||||
|
||||
if (opts.arch) {
|
||||
args.push('-PcdvBuildArch=' + opts.arch);
|
||||
}
|
||||
}
|
||||
|
||||
args.push.apply(args, opts.extraArgs);
|
||||
args.push(buildCmd);
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
/*
|
||||
* This returns a promise
|
||||
*/
|
||||
runGradleWrapper (gradle_cmd) {
|
||||
const gradlePath = path.join(this.root, 'gradlew');
|
||||
const wrapperGradle = path.join(this.root, 'wrapper.gradle');
|
||||
if (fs.existsSync(gradlePath)) {
|
||||
// Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
|
||||
getGradleWrapperPath () {
|
||||
let wrapper = path.join(this.root, 'tools', 'gradlew');
|
||||
|
||||
if (isWindows()) {
|
||||
wrapper += '.bat';
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs/updates the gradle wrapper
|
||||
* @param {string} gradleVersion The gradle version to install. Ignored if CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL environment variable is defined
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async installGradleWrapper (gradleVersion) {
|
||||
if (process.env.CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL) {
|
||||
events.emit('verbose', `Overriding Gradle Version via CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL (${process.env.CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL})`);
|
||||
await execa('gradle', ['-p', path.join(this.root, 'tools'), 'wrapper', '--gradle-distribution-url', process.env.CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL], { stdio: 'inherit' });
|
||||
} else {
|
||||
return execa(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], { stdio: 'inherit' });
|
||||
await execa('gradle', ['-p', path.join(this.root, 'tools'), 'wrapper', '--gradle-version', gradleVersion], { stdio: 'inherit' });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,19 +157,6 @@ class ProjectBuilder {
|
||||
};
|
||||
}
|
||||
|
||||
extractRealProjectNameFromManifest () {
|
||||
const manifestPath = path.join(this.root, 'app', 'src', 'main', 'AndroidManifest.xml');
|
||||
const manifestData = fs.readFileSync(manifestPath, 'utf8');
|
||||
const m = /<manifest[\s\S]*?package\s*=\s*"(.*?)"/i.exec(manifestData);
|
||||
if (!m) {
|
||||
throw new CordovaError('Could not find package name in ' + manifestPath);
|
||||
}
|
||||
|
||||
const packageName = m[1];
|
||||
const lastDotIndex = packageName.lastIndexOf('.');
|
||||
return packageName.substring(lastDotIndex + 1);
|
||||
}
|
||||
|
||||
// Makes the project buildable, minus the gradle wrapper.
|
||||
prepBuildFiles () {
|
||||
// Update the version of build.gradle in each dependent library.
|
||||
@@ -184,7 +183,11 @@ class ProjectBuilder {
|
||||
checkAndCopy(subProjects[i], this.root);
|
||||
}
|
||||
}
|
||||
const projectName = this.extractRealProjectNameFromManifest();
|
||||
|
||||
// get project name cdv-gradle-config.
|
||||
const cdvGradleConfig = CordovaGradleConfigParserFactory.create(this.root);
|
||||
const projectName = cdvGradleConfig.getProjectNameFromPackageName();
|
||||
|
||||
// Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
|
||||
const settingsGradlePaths = subProjects.map(function (p) {
|
||||
const realDir = p.replace(/[/\\]/g, ':');
|
||||
@@ -281,23 +284,14 @@ class ProjectBuilder {
|
||||
|
||||
prepEnv (opts) {
|
||||
const self = this;
|
||||
const config = this._getCordovaConfig();
|
||||
return check_reqs.check_gradle()
|
||||
.then(function (gradlePath) {
|
||||
return self.runGradleWrapper(gradlePath);
|
||||
.then(function () {
|
||||
events.emit('verbose', `Using Gradle: ${config.GRADLE_VERSION}`);
|
||||
return self.installGradleWrapper(config.GRADLE_VERSION);
|
||||
}).then(function () {
|
||||
return self.prepBuildFiles();
|
||||
}).then(() => {
|
||||
const config = this._getCordovaConfig();
|
||||
// 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-${config.GRADLE_VERSION}-all.zip`;
|
||||
gradleWrapperProperties.set('distributionUrl', distributionUrl);
|
||||
gradleWrapperProperties.save();
|
||||
|
||||
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);
|
||||
@@ -323,9 +317,11 @@ class ProjectBuilder {
|
||||
* Returns a promise.
|
||||
*/
|
||||
async build (opts) {
|
||||
const wrapper = path.join(this.root, 'gradlew');
|
||||
const wrapper = this.getGradleWrapperPath();
|
||||
const args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
|
||||
|
||||
events.emit('verbose', `Running Gradle Build: ${wrapper} ${args.join(' ')}`);
|
||||
|
||||
try {
|
||||
return await execa(wrapper, args, { stdio: 'inherit', cwd: path.resolve(this.root) });
|
||||
} catch (error) {
|
||||
@@ -342,7 +338,7 @@ class ProjectBuilder {
|
||||
}
|
||||
|
||||
clean (opts) {
|
||||
const wrapper = path.join(this.root, 'gradlew');
|
||||
const wrapper = this.getGradleWrapperPath();
|
||||
const args = this.getArgs('clean', opts);
|
||||
return execa(wrapper, args, { stdio: 'inherit', cwd: path.resolve(this.root) })
|
||||
.then(() => {
|
||||
|
||||
@@ -149,7 +149,7 @@ module.exports.check_gradle = function () {
|
||||
const sdkDir = process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT;
|
||||
if (!sdkDir) {
|
||||
return Promise.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Could not find Android SDK directory.\n' +
|
||||
'Might need to install Android SDK or set up \'ANDROID_SDK_ROOT\' env variable.'));
|
||||
'Might need to install Android SDK or set up \'ANDROID_HOME\' env variable.'));
|
||||
}
|
||||
|
||||
const gradlePath = module.exports.get_gradle_wrapper();
|
||||
@@ -270,7 +270,7 @@ module.exports.check_android = function () {
|
||||
'Failed to find \'android\' command in your \'PATH\'. Try update your \'PATH\' to include path to valid SDK directory.');
|
||||
}
|
||||
if (!fs.existsSync(process.env.ANDROID_HOME)) {
|
||||
throw new CordovaError('\'ANDROID_HOME\' environment variable is set to non-existent path: ' + process.env.ANDROID_SDK_ROOT +
|
||||
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
|
||||
@@ -306,7 +306,7 @@ module.exports.run = function () {
|
||||
console.log('ANDROID_SDK_ROOT=' + process.env.ANDROID_SDK_ROOT + ' (DEPRECATED)');
|
||||
|
||||
return Promise.all([this.check_java(), this.check_android()]).then(function (values) {
|
||||
console.log('Using Android SDK: ' + process.env.ANDROID_SDK_ROOT);
|
||||
console.log('Using Android SDK: ' + (process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT));
|
||||
|
||||
if (!values[1]) {
|
||||
throw new CordovaError('Requirements check failed for Android SDK! Android SDK was not detected.');
|
||||
@@ -327,7 +327,7 @@ const Requirement = function (id, name, version, installed) {
|
||||
this.name = name;
|
||||
this.installed = installed || false;
|
||||
this.metadata = {
|
||||
version: version
|
||||
version
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
71
lib/config/CordovaGradleConfigParser.js
Normal file
71
lib/config/CordovaGradleConfigParser.js
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const events = require('cordova-common').events;
|
||||
|
||||
class CordovaGradleConfigParser {
|
||||
/**
|
||||
* Loads and Edits Gradle Properties File.
|
||||
*
|
||||
* Do not construct this directly. Use CordovaGradleConfigParserFactory instead.
|
||||
*
|
||||
* @param {String} platformDir is the path of the Android platform directory
|
||||
*/
|
||||
constructor (platformDir) {
|
||||
this._cdvGradleConfigFilePath = path.join(platformDir, 'cdv-gradle-config.json');
|
||||
this._cdvGradleConfig = this._readConfig(this._cdvGradleConfigFilePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and parses the configuration JSON file
|
||||
*
|
||||
* @param {String} configPath
|
||||
* @returns {Record<any, any>} The parsed JSON object representing the gradle config.
|
||||
*/
|
||||
_readConfig (configPath) {
|
||||
return fs.readJSONSync(configPath, 'utf-8');
|
||||
}
|
||||
|
||||
setPackageName (packageName) {
|
||||
events.emit('verbose', '[Cordova Gradle Config] Setting "PACKAGE_NAMESPACE" to ' + packageName);
|
||||
this._cdvGradleConfig.PACKAGE_NAMESPACE = packageName;
|
||||
return this;
|
||||
}
|
||||
|
||||
getPackageName () {
|
||||
return this._cdvGradleConfig.PACKAGE_NAMESPACE;
|
||||
}
|
||||
|
||||
getProjectNameFromPackageName () {
|
||||
const packageName = this._cdvGradleConfig.PACKAGE_NAMESPACE;
|
||||
return packageName.substring(packageName.lastIndexOf('.') + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves any changes that has been made to the properties file.
|
||||
*/
|
||||
write () {
|
||||
events.emit('verbose', '[Cordova Gradle Config] Saving File');
|
||||
fs.writeJSONSync(this._cdvGradleConfigFilePath, this._cdvGradleConfig, 'utf-8');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CordovaGradleConfigParser;
|
||||
34
lib/config/CordovaGradleConfigParserFactory.js
Normal file
34
lib/config/CordovaGradleConfigParserFactory.js
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const CordovaGradleConfigParser = require('./CordovaGradleConfigParser');
|
||||
|
||||
/**
|
||||
* Builds new gradle config parsers
|
||||
*/
|
||||
module.exports = class CordovaGradleConfigParserFactory {
|
||||
/**
|
||||
* Loads and Edits Gradle Properties File.
|
||||
*
|
||||
* @param {String} platformDir is the path of the Android platform directory
|
||||
*/
|
||||
static create (platformDir) {
|
||||
return new CordovaGradleConfigParser(platformDir);
|
||||
}
|
||||
};
|
||||
@@ -23,6 +23,7 @@ const utils = require('./utils');
|
||||
const check_reqs = require('./check_reqs');
|
||||
const ROOT = path.join(__dirname, '..');
|
||||
const { createEditor } = require('properties-parser');
|
||||
const CordovaGradleConfigParserFactory = require('./config/CordovaGradleConfigParserFactory');
|
||||
|
||||
const CordovaError = require('cordova-common').CordovaError;
|
||||
const AndroidManifest = require('./AndroidManifest');
|
||||
@@ -112,20 +113,15 @@ function prepBuildFiles (projectPath) {
|
||||
buildModule.getBuilder(projectPath).prepBuildFiles();
|
||||
}
|
||||
|
||||
function copyBuildRules (projectPath, isLegacy) {
|
||||
function copyBuildRules (projectPath) {
|
||||
const srcDir = path.join(ROOT, 'templates', 'project');
|
||||
|
||||
if (isLegacy) {
|
||||
// The project's build.gradle is identical to the earlier build.gradle, so it should still work
|
||||
fs.copySync(path.join(srcDir, 'legacy', 'build.gradle'), path.join(projectPath, 'legacy', 'build.gradle'));
|
||||
fs.copySync(path.join(srcDir, 'wrapper.gradle'), path.join(projectPath, 'wrapper.gradle'));
|
||||
} else {
|
||||
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, 'app', 'repositories.gradle'), path.join(projectPath, 'app', 'repositories.gradle'));
|
||||
fs.copySync(path.join(srcDir, 'repositories.gradle'), path.join(projectPath, 'repositories.gradle'));
|
||||
fs.copySync(path.join(srcDir, 'wrapper.gradle'), path.join(projectPath, 'wrapper.gradle'));
|
||||
}
|
||||
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, 'app', 'repositories.gradle'), path.join(projectPath, 'app', 'repositories.gradle'));
|
||||
fs.copySync(path.join(srcDir, 'repositories.gradle'), path.join(projectPath, 'repositories.gradle'));
|
||||
|
||||
copyGradleTools(projectPath);
|
||||
}
|
||||
|
||||
function copyScripts (projectPath) {
|
||||
@@ -175,6 +171,12 @@ function validateProjectName (project_name) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function copyGradleTools (projectPath) {
|
||||
const srcDir = path.join(ROOT, 'templates', 'project');
|
||||
|
||||
fs.copySync(path.resolve(srcDir, 'tools'), path.resolve(projectPath, 'tools'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an android application with the given options.
|
||||
*
|
||||
@@ -249,6 +251,11 @@ exports.create = function (project_path, config, options, events) {
|
||||
fs.ensureDirSync(assets_path);
|
||||
fs.ensureDirSync(resource_path);
|
||||
|
||||
// store package name in cdv-gradle-config
|
||||
const cdvGradleConfig = CordovaGradleConfigParserFactory.create(project_path);
|
||||
cdvGradleConfig.setPackageName(package_name)
|
||||
.write();
|
||||
|
||||
// interpolate the activity name and package
|
||||
const packagePath = package_name.replace(/\./g, path.sep);
|
||||
const activity_dir = path.join(java_path, packagePath);
|
||||
@@ -261,8 +268,7 @@ exports.create = function (project_path, config, options, events) {
|
||||
utils.replaceFileContents(activity_path, /__ID__/, package_name);
|
||||
|
||||
const manifest = new AndroidManifest(path.join(project_template_dir, 'AndroidManifest.xml'));
|
||||
manifest.setPackageId(package_name)
|
||||
.getActivity().setName(safe_activity_name);
|
||||
manifest.getActivity().setName(safe_activity_name);
|
||||
|
||||
const manifest_path = path.join(app_path, 'AndroidManifest.xml');
|
||||
manifest.write(manifest_path);
|
||||
|
||||
149
lib/prepare.js
149
lib/prepare.js
@@ -21,6 +21,7 @@ const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const nopt = require('nopt');
|
||||
const glob = require('fast-glob');
|
||||
const dedent = require('dedent');
|
||||
const events = require('cordova-common').events;
|
||||
const AndroidManifest = require('./AndroidManifest');
|
||||
const xmlHelpers = require('cordova-common').xmlHelpers;
|
||||
@@ -34,6 +35,7 @@ const utils = require('./utils');
|
||||
const gradleConfigDefaults = require('./gradle-config-defaults');
|
||||
const checkReqs = require('./check_reqs');
|
||||
const GradlePropertiesParser = require('./config/GradlePropertiesParser');
|
||||
const CordovaGradleConfigParserFactory = require('./config/CordovaGradleConfigParserFactory');
|
||||
|
||||
function parseArguments (argv) {
|
||||
return nopt({
|
||||
@@ -108,7 +110,10 @@ function getUserGradleConfig (configXml) {
|
||||
{ xmlKey: 'AndroidXWebKitVersion', gradleKey: 'ANDROIDX_WEBKIT_VERSION', type: String },
|
||||
{ xmlKey: 'GradlePluginGoogleServicesVersion', gradleKey: 'GRADLE_PLUGIN_GOOGLE_SERVICES_VERSION', type: String },
|
||||
{ xmlKey: 'GradlePluginGoogleServicesEnabled', gradleKey: 'IS_GRADLE_PLUGIN_GOOGLE_SERVICES_ENABLED', type: Boolean },
|
||||
{ xmlKey: 'GradlePluginKotlinEnabled', gradleKey: 'IS_GRADLE_PLUGIN_KOTLIN_ENABLED', type: Boolean }
|
||||
{ xmlKey: 'GradlePluginKotlinEnabled', gradleKey: 'IS_GRADLE_PLUGIN_KOTLIN_ENABLED', type: Boolean },
|
||||
{ xmlKey: 'AndroidJavaSourceCompatibility', gradleKey: 'JAVA_SOURCE_COMPATIBILITY', type: Number },
|
||||
{ xmlKey: 'AndroidJavaTargetCompatibility', gradleKey: 'JAVA_TARGET_COMPATIBILITY', type: Number },
|
||||
{ xmlKey: 'AndroidKotlinJVMTarget', gradleKey: 'KOTLIN_JVM_TARGET', type: String }
|
||||
];
|
||||
|
||||
return configXmlToGradleMapping.reduce((config, mapping) => {
|
||||
@@ -278,20 +283,22 @@ function updateProjectAccordingTo (platformConfig, locations) {
|
||||
// Java packages cannot support dashes
|
||||
const androidPkgName = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_');
|
||||
|
||||
const manifest = new AndroidManifest(locations.manifest);
|
||||
const manifestId = manifest.getPackageId();
|
||||
// updating cdv-gradle-config with new androidPkgName.
|
||||
const cdvGradleConfig = CordovaGradleConfigParserFactory.create(locations.root);
|
||||
cdvGradleConfig.setPackageName(androidPkgName)
|
||||
.write();
|
||||
|
||||
const manifest = new AndroidManifest(locations.manifest);
|
||||
manifest.getActivity()
|
||||
.setOrientation(platformConfig.getPreference('orientation'))
|
||||
.setLaunchMode(findAndroidLaunchModePreference(platformConfig));
|
||||
|
||||
manifest.setVersionName(platformConfig.version())
|
||||
.setVersionCode(platformConfig.android_versionCode() || default_versionCode(platformConfig.version()))
|
||||
.setPackageId(androidPkgName)
|
||||
.write();
|
||||
|
||||
// Java file paths shouldn't be hard coded
|
||||
const javaDirectory = path.join(locations.javaSrc, manifestId.replace(/\./g, '/'));
|
||||
const javaDirectory = path.join(locations.javaSrc, androidPkgName.replace(/\./g, '/'));
|
||||
const java_files = glob.sync('**/*.java', { cwd: javaDirectory, absolute: true }).filter(f => {
|
||||
const contents = fs.readFileSync(f, 'utf-8');
|
||||
return /extends\s+CordovaActivity/.test(contents);
|
||||
@@ -374,27 +381,34 @@ function updateProjectSplashScreen (platformConfig, locations) {
|
||||
const themes = xmlHelpers.parseElementtreeSync(locations.themes);
|
||||
const splashScreenTheme = themes.find('style[@name="Theme.App.SplashScreen"]');
|
||||
|
||||
let splashBg = platformConfig.getPreference('AndroidWindowSplashScreenBackground', this.platform);
|
||||
if (!splashBg) {
|
||||
splashBg = platformConfig.getPreference('SplashScreenBackgroundColor', this.platform);
|
||||
}
|
||||
if (!splashBg) {
|
||||
splashBg = platformConfig.getPreference('BackgroundColor', this.platform);
|
||||
}
|
||||
|
||||
// use the user defined value for "colors.xml"
|
||||
updateProjectSplashScreenBackgroundColor(splashBg, locations);
|
||||
|
||||
// force the themes value to `@color/cdv_splashscreen_background`
|
||||
const splashBgNode = splashScreenTheme.find('item[@name="windowSplashScreenBackground"]');
|
||||
splashBgNode.text = '@color/cdv_splashscreen_background';
|
||||
|
||||
[
|
||||
'windowSplashScreenAnimatedIcon',
|
||||
'windowSplashScreenAnimationDuration',
|
||||
'windowSplashScreenBackground',
|
||||
'windowSplashScreenBrandingImage',
|
||||
'android:windowSplashScreenBrandingImage',
|
||||
'windowSplashScreenIconBackgroundColor',
|
||||
'postSplashScreenTheme'
|
||||
].forEach(themeKey => {
|
||||
const cdvConfigPrefKey = 'Android' + themeKey.charAt(0).toUpperCase() + themeKey.slice(1);
|
||||
const index = themeKey.indexOf(':') + 1;
|
||||
const cdvConfigPrefKey = 'Android' + themeKey.charAt(index).toUpperCase() + themeKey.slice(index + 1);
|
||||
const cdvConfigPrefValue = platformConfig.getPreference(cdvConfigPrefKey, this.platform);
|
||||
let themeTargetNode = splashScreenTheme.find(`item[@name="${themeKey}"]`);
|
||||
|
||||
switch (themeKey) {
|
||||
case 'windowSplashScreenBackground':
|
||||
// use the user defined value for "colors.xml"
|
||||
updateProjectSplashScreenBackgroundColor(cdvConfigPrefValue, locations);
|
||||
|
||||
// force the themes value to `@color/cdv_splashscreen_background`
|
||||
themeTargetNode.text = '@color/cdv_splashscreen_background';
|
||||
break;
|
||||
|
||||
case 'windowSplashScreenAnimatedIcon':
|
||||
// handle here the cases of "png" vs "xml" (drawable)
|
||||
// If "png":
|
||||
@@ -411,7 +425,7 @@ function updateProjectSplashScreen (platformConfig, locations) {
|
||||
updateProjectSplashScreenImage(locations, themeKey, cdvConfigPrefKey, cdvConfigPrefValue);
|
||||
break;
|
||||
|
||||
case 'windowSplashScreenBrandingImage':
|
||||
case 'android:windowSplashScreenBrandingImage':
|
||||
// display warning only when set.
|
||||
if (cdvConfigPrefValue) {
|
||||
events.emit('warn', `"${themeKey}" is currently not supported by the splash screen compatibility library. https://issuetracker.google.com/issues/194301890`);
|
||||
@@ -422,13 +436,14 @@ function updateProjectSplashScreen (platformConfig, locations) {
|
||||
// force the themes value to `@color/cdv_splashscreen_icon_background`
|
||||
if (!cdvConfigPrefValue && themeTargetNode) {
|
||||
splashScreenTheme.remove(themeTargetNode);
|
||||
delete themes.getroot().attrib['xmlns:tools'];
|
||||
} else if (cdvConfigPrefValue) {
|
||||
// if there is no current node, create a new node.
|
||||
if (!themeTargetNode) {
|
||||
themeTargetNode = themes.getroot().makeelement('item', { name: themeKey });
|
||||
themeTargetNode = themes.getroot().makeelement('item', { name: themeKey, 'tools:targetApi': '31' });
|
||||
splashScreenTheme.append(themeTargetNode);
|
||||
themes.getroot().attrib['xmlns:tools'] = 'http://schemas.android.com/tools';
|
||||
}
|
||||
|
||||
// set the user defined color.
|
||||
themeTargetNode.text = '@drawable/ic_cdv_splashscreen_branding';
|
||||
}
|
||||
@@ -537,7 +552,7 @@ function cleanupAndSetProjectSplashScreenImage (srcFile, destFilePath, possibleP
|
||||
function updateProjectSplashScreenImage (locations, themeKey, cdvConfigPrefKey, cdvConfigPrefValue = '') {
|
||||
const SPLASH_SCREEN_IMAGE_BY_THEME_KEY = {
|
||||
windowSplashScreenAnimatedIcon: 'ic_cdv_splashscreen',
|
||||
windowSplashScreenBrandingImage: 'ic_cdv_splashscreen_branding'
|
||||
'android:windowSplashScreenBrandingImage': 'ic_cdv_splashscreen_branding'
|
||||
};
|
||||
|
||||
const destFileName = SPLASH_SCREEN_IMAGE_BY_THEME_KEY[themeKey] || null;
|
||||
@@ -555,7 +570,7 @@ function updateProjectSplashScreenImage (locations, themeKey, cdvConfigPrefKey,
|
||||
// Default Drawable Source File
|
||||
let defaultSrcFilePath = null;
|
||||
|
||||
if (themeKey !== 'windowSplashScreenBrandingImage') {
|
||||
if (themeKey !== 'android:windowSplashScreenBrandingImage') {
|
||||
try {
|
||||
// coming from user project
|
||||
defaultSrcFilePath = require.resolve('cordova-android/templates/project/res/drawable/' + destFileNameExt);
|
||||
@@ -575,7 +590,7 @@ function updateProjectSplashScreenImage (locations, themeKey, cdvConfigPrefKey,
|
||||
}
|
||||
|
||||
events.emit(emitType, emmitMessage);
|
||||
const cleanupOnly = themeKey === 'windowSplashScreenBrandingImage';
|
||||
const cleanupOnly = themeKey === 'android:windowSplashScreenBrandingImage';
|
||||
cleanupAndSetProjectSplashScreenImage(defaultSrcFilePath, destFilePath, possiblePreviousDestFilePath, cleanupOnly);
|
||||
return;
|
||||
}
|
||||
@@ -701,8 +716,10 @@ function updateIcons (cordovaProject, platformResourcesDir) {
|
||||
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher.png'),
|
||||
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_foreground.png'),
|
||||
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_background.png'),
|
||||
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_monochrome.png'),
|
||||
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_foreground.xml'),
|
||||
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_background.xml'),
|
||||
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_monochrome.xml'),
|
||||
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher.xml')
|
||||
);
|
||||
|
||||
@@ -726,17 +743,38 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
|
||||
// project's config.xml location, so we use it as base path.
|
||||
let background;
|
||||
let foreground;
|
||||
let monochrome;
|
||||
let targetPathBackground;
|
||||
let targetPathForeground;
|
||||
let targetPathMonochrome;
|
||||
|
||||
for (const density in android_icons) {
|
||||
let backgroundVal = '@mipmap/ic_launcher_background';
|
||||
let foregroundVal = '@mipmap/ic_launcher_foreground';
|
||||
const monochromeVal = '@mipmap/ic_launcher_monochrome';
|
||||
|
||||
background = android_icons[density].background;
|
||||
foreground = android_icons[density].foreground;
|
||||
monochrome = android_icons[density].monochrome;
|
||||
|
||||
if (!background || !foreground) {
|
||||
const hasAdaptiveIcons = !!background && !!foreground;
|
||||
let hasMonochromeIcon = !!monochrome;
|
||||
|
||||
if (hasMonochromeIcon && !hasAdaptiveIcons) {
|
||||
// If we have a monochrome icon, but no adaptive icons,
|
||||
// then warn that in order to use monochrome, the adaptive icons
|
||||
// must be supplied. We will ignore monochrome and proceed with the
|
||||
// icon preparation however.
|
||||
hasMonochromeIcon = false;
|
||||
monochrome = undefined;
|
||||
events.emit('warn', dedent`
|
||||
Monochrome icon found but without adaptive properties.
|
||||
Monochrome icon requires the adaptive background and foreground assets.
|
||||
See https://cordova.apache.org/docs/en/latest/config_ref/images.html fore more information.
|
||||
`);
|
||||
}
|
||||
|
||||
if (!hasAdaptiveIcons) {
|
||||
// This icon isn't an adaptive icon, so skip it
|
||||
continue;
|
||||
}
|
||||
@@ -767,12 +805,38 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
|
||||
resourceMap[targetPathForeground] = android_icons[density].foreground;
|
||||
}
|
||||
|
||||
if (hasMonochromeIcon) {
|
||||
if (path.extname(path.basename(monochrome)) === '.xml') {
|
||||
// Vector Use Case
|
||||
targetPathMonochrome = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher_monochrome.xml', path.basename(android_icons[density].monochrome));
|
||||
resourceMap[targetPathMonochrome] = android_icons[density].monochrome;
|
||||
} else if (path.extname(path.basename(monochrome)) === '.png') {
|
||||
// Images Use Case
|
||||
targetPathMonochrome = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher_monochrome.png', path.basename(android_icons[density].monochrome));
|
||||
resourceMap[targetPathMonochrome] = android_icons[density].monochrome;
|
||||
}
|
||||
}
|
||||
|
||||
// create an XML for DPI and set color
|
||||
const icLauncherTemplate = `<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="` + backgroundVal + `" />
|
||||
<foreground android:drawable="` + foregroundVal + `" />
|
||||
</adaptive-icon>`;
|
||||
let icLauncherTemplate = '';
|
||||
if (hasMonochromeIcon) {
|
||||
icLauncherTemplate = dedent`
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="${backgroundVal}" />
|
||||
<foreground android:drawable="${foregroundVal}" />
|
||||
<monochrome android:drawable="${monochromeVal}" />
|
||||
</adaptive-icon>
|
||||
`;
|
||||
} else {
|
||||
icLauncherTemplate = dedent`
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="${backgroundVal}" />
|
||||
<foreground android:drawable="${foregroundVal}" />
|
||||
</adaptive-icon>
|
||||
`;
|
||||
}
|
||||
|
||||
const launcherXmlPath = path.join(platformResourcesDir, 'mipmap-' + density + '-v26', 'ic_launcher.xml');
|
||||
|
||||
@@ -786,6 +850,7 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
|
||||
if (default_icon && !android_icons.mdpi) {
|
||||
let defaultTargetPathBackground;
|
||||
let defaultTargetPathForeground;
|
||||
let defaultTargetPathMonochrome;
|
||||
|
||||
if (background.startsWith('@color')) {
|
||||
// Colors Use Case
|
||||
@@ -812,6 +877,18 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
|
||||
defaultTargetPathForeground = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher_foreground.png', path.basename(default_icon.foreground));
|
||||
resourceMap[defaultTargetPathForeground] = default_icon.foreground;
|
||||
}
|
||||
|
||||
if (monochrome) {
|
||||
if (path.extname(path.basename(monochrome)) === '.xml') {
|
||||
// Vector Use Case
|
||||
defaultTargetPathMonochrome = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher_monochrome.xml', path.basename(default_icon.monochrome));
|
||||
resourceMap[defaultTargetPathMonochrome] = default_icon.monochrome;
|
||||
} else if (path.extname(path.basename(monochrome)) === '.png') {
|
||||
// Images Use Case
|
||||
defaultTargetPathMonochrome = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher_monochrome.png', path.basename(default_icon.monochrome));
|
||||
resourceMap[defaultTargetPathMonochrome] = default_icon.monochrome;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resourceMap;
|
||||
@@ -882,6 +959,11 @@ function prepareIcons (icons) {
|
||||
const favor = {};
|
||||
|
||||
// populating found icon.
|
||||
if (icon.background && icon.foreground && icon.monochrome) {
|
||||
found.background = icon.background;
|
||||
found.foreground = icon.foreground;
|
||||
found.monochrome = icon.monochrome;
|
||||
}
|
||||
if (icon.background && icon.foreground) {
|
||||
found.background = icon.background;
|
||||
found.foreground = icon.foreground;
|
||||
@@ -890,6 +972,11 @@ function prepareIcons (icons) {
|
||||
found.src = icon.src;
|
||||
}
|
||||
|
||||
if (default_icon.background && default_icon.foreground && default_icon.monochrome) {
|
||||
favor.background = default_icon.background;
|
||||
favor.foreground = default_icon.foreground;
|
||||
favor.monochrome = default_icon.monochrome;
|
||||
}
|
||||
if (default_icon.background && default_icon.foreground) {
|
||||
favor.background = default_icon.background;
|
||||
favor.foreground = default_icon.foreground;
|
||||
@@ -908,8 +995,8 @@ function prepareIcons (icons) {
|
||||
}
|
||||
|
||||
return {
|
||||
android_icons: android_icons,
|
||||
default_icon: default_icon
|
||||
android_icons,
|
||||
default_icon
|
||||
};
|
||||
}
|
||||
|
||||
@@ -927,8 +1014,10 @@ function cleanIcons (projectRoot, projectConfig, platformResourcesDir) {
|
||||
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher.png'),
|
||||
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_foreground.png'),
|
||||
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_background.png'),
|
||||
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_monochrome.png'),
|
||||
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_foreground.xml'),
|
||||
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_background.xml'),
|
||||
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_monochrome.xml'),
|
||||
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher.xml')
|
||||
);
|
||||
|
||||
|
||||
50
lib/run.js
50
lib/run.js
@@ -23,6 +23,7 @@ const build = require('./build');
|
||||
const PackageType = require('./PackageType');
|
||||
const AndroidManifest = require('./AndroidManifest');
|
||||
const { CordovaError, events } = require('cordova-common');
|
||||
const CordovaGradleConfigParserFactory = require('./config/CordovaGradleConfigParserFactory');
|
||||
|
||||
/**
|
||||
* Builds a target spec from a runOptions object
|
||||
@@ -78,6 +79,53 @@ module.exports.run = async function (runOptions = {}) {
|
||||
}
|
||||
|
||||
const manifest = new AndroidManifest(this.locations.manifest);
|
||||
const cordovaGradleConfigParser = CordovaGradleConfigParserFactory.create(this.locations.root);
|
||||
|
||||
return target.install(resolvedTarget, { manifest, buildResults });
|
||||
return target.install(resolvedTarget, { manifest, buildResults, cordovaGradleConfigParser });
|
||||
};
|
||||
|
||||
module.exports.listDevices = async function () {
|
||||
events.emit('log', `\nAvailable ${this.platform} devices:`);
|
||||
|
||||
const { list } = require('./target');
|
||||
|
||||
await list().then(targets => {
|
||||
const deviceIds = targets
|
||||
.filter(({ type }) => type === 'device')
|
||||
.map(({ id }) => id);
|
||||
|
||||
console.log(deviceIds.join('\n'));
|
||||
}, function (err) {
|
||||
console.error('ERROR: ' + err);
|
||||
process.exit(2);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.listEmulators = async function () {
|
||||
events.emit('log', `\nAvailable ${this.platform} virtual devices:`);
|
||||
const emulators = require('./emulator');
|
||||
|
||||
await emulators.list_images().then(function (emulator_list) {
|
||||
emulator_list && emulator_list.forEach(function (emu) {
|
||||
console.log(emu.name);
|
||||
});
|
||||
}, function (err) {
|
||||
console.error('ERROR: ' + err);
|
||||
process.exit(2);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.runListDevices = async function (options = {}) {
|
||||
const { options: cliArgs = {} } = options;
|
||||
|
||||
if (cliArgs?.device) {
|
||||
await module.exports.listDevices.call(this);
|
||||
} else if (cliArgs?.emulator) {
|
||||
await module.exports.listEmulators.call(this);
|
||||
} else {
|
||||
await module.exports.listDevices.call(this);
|
||||
await module.exports.listEmulators.call(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -127,9 +127,9 @@ exports.resolve = async (spec, buildResults) => {
|
||||
};
|
||||
};
|
||||
|
||||
exports.install = async function ({ id: target, arch, type }, { manifest, buildResults }) {
|
||||
exports.install = async function ({ id: target, arch, type }, { manifest, buildResults, cordovaGradleConfigParser }) {
|
||||
const apk_path = build.findBestApkForArchitecture(buildResults, arch);
|
||||
const pkgName = manifest.getPackageId();
|
||||
const pkgName = cordovaGradleConfigParser.getPackageName();
|
||||
const launchName = pkgName + '/.' + manifest.getActivity().getName();
|
||||
|
||||
events.emit('log', 'Using apk: ' + apk_path);
|
||||
|
||||
60
licence_checker.yml
Normal file
60
licence_checker.yml
Normal file
@@ -0,0 +1,60 @@
|
||||
# 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.
|
||||
|
||||
# Compiled list of allowed 3RD PARTY LICENSES from:
|
||||
#
|
||||
# ASF CATEGORY A: WHAT CAN WE INCLUDE IN AN ASF PROJECT
|
||||
# https://www.apache.org/legal/resolved.html#category-a
|
||||
#
|
||||
# Licenses converted into the SPDX standardized short identifier format.
|
||||
# https://spdx.org/licenses/
|
||||
allowed-licenses:
|
||||
- 0BSD
|
||||
- AFL-3.0
|
||||
- Apache-1.1
|
||||
- Apache-2.0
|
||||
- APAFML
|
||||
- BlueOak-1.0.0
|
||||
- BSD-2-Clause
|
||||
- BSD-3-Clause
|
||||
- BSD-3-Clause-LBNL
|
||||
- BSL-1.0
|
||||
- CC-PDDC
|
||||
- CC0-1.0
|
||||
- EPICS
|
||||
- HPND
|
||||
- ICU
|
||||
- ISC
|
||||
- MIT
|
||||
- MIT-0
|
||||
- MS-PL
|
||||
- MulanPSL-2.0
|
||||
- NCSA
|
||||
- OGL-UK-3.0
|
||||
- PHP-3.01
|
||||
- PostgreSQL
|
||||
- PSF-2.0
|
||||
- SMLNJ
|
||||
- Unicode-DFS-2016
|
||||
- Unlicense
|
||||
- UPL-1.0
|
||||
- W3C
|
||||
- WTFPL
|
||||
- X11
|
||||
- Xnet
|
||||
- Zlib
|
||||
- ZPL-2.0
|
||||
6344
package-lock.json
generated
6344
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
33
package.json
33
package.json
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"name": "cordova-android",
|
||||
"version": "11.0.0",
|
||||
"version": "13.0.0",
|
||||
"description": "cordova-android release",
|
||||
"types": "./types/index.d.ts",
|
||||
"main": "lib/Api.js",
|
||||
"repository": "github:apache/cordova-android",
|
||||
"bugs": "https://github.com/apache/cordova-android/issues",
|
||||
@@ -14,38 +15,40 @@
|
||||
"prepare": "cordova-js build > templates/project/assets/www/cordova.js",
|
||||
"test": "npm run lint && npm run cover && npm run java-unit-tests",
|
||||
"lint": "eslint lib spec test \"templates/cordova/**/!(*.*)\"",
|
||||
"lint:fix": "npm run lint -- --fix",
|
||||
"unit-tests": "jasmine --config=spec/unit/jasmine.json",
|
||||
"cover": "nyc jasmine --config=spec/coverage.json",
|
||||
"e2e-tests": "jasmine --config=spec/e2e/jasmine.json",
|
||||
"java-unit-tests": "node test/run_java_unit_tests.js",
|
||||
"clean:java-unit-tests": "node test/clean.js"
|
||||
"java-unit-tests": "node test/run_java_unit_tests.js"
|
||||
},
|
||||
"author": "Apache Software Foundation",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"android-versions": "^1.7.0",
|
||||
"cordova-common": "^4.0.2",
|
||||
"android-versions": "^2.0.0",
|
||||
"cordova-common": "^5.0.0",
|
||||
"dedent": "^1.5.3",
|
||||
"execa": "^5.1.1",
|
||||
"fast-glob": "^3.2.11",
|
||||
"fs-extra": "^10.1.0",
|
||||
"fast-glob": "^3.3.2",
|
||||
"fs-extra": "^11.2.0",
|
||||
"is-path-inside": "^3.0.3",
|
||||
"nopt": "^5.0.0",
|
||||
"properties-parser": "^0.3.1",
|
||||
"semver": "^7.3.7",
|
||||
"nopt": "^7.2.1",
|
||||
"properties-parser": "^0.6.0",
|
||||
"semver": "^7.6.2",
|
||||
"string-argv": "^0.3.1",
|
||||
"untildify": "^4.0.0",
|
||||
"which": "^2.0.2"
|
||||
"which": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cordova/eslint-config": "^4.0.0",
|
||||
"@cordova/eslint-config": "^5.1.0",
|
||||
"cordova-js": "^6.1.0",
|
||||
"elementtree": "^0.1.7",
|
||||
"jasmine": "^4.2.1",
|
||||
"jasmine": "^5.1.0",
|
||||
"jasmine-spec-reporter": "^7.0.0",
|
||||
"nyc": "^15.1.0",
|
||||
"rewire": "^6.0.0"
|
||||
"rewire": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
"node": ">=16.13.0"
|
||||
},
|
||||
"nyc": {
|
||||
"include": [
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
package="com.example.anis.myapplication">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
# 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.
|
||||
|
||||
#Mon Dec 28 10:00:20 PST 2015
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
|
||||
|
||||
@@ -110,18 +110,6 @@ describe('AndroidManifest', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('packageId', () => {
|
||||
it('should get the package ID', () => {
|
||||
expect(manifest.getPackageId()).toBe(PACKAGE_ID);
|
||||
});
|
||||
|
||||
it('should set the package ID', () => {
|
||||
const newPackageId = `${PACKAGE_ID}new`;
|
||||
manifest.setPackageId(newPackageId);
|
||||
expect(manifest.getPackageId()).toBe(newPackageId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('activity', () => {
|
||||
let activity;
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
const path = require('path');
|
||||
const rewire = require('rewire');
|
||||
const MockCordovaGradleConfigParser = require('./mocks/config/MockCordovaGradleConfigParser');
|
||||
const CordovaGradleConfigParserFactory = require('../../lib/config/CordovaGradleConfigParserFactory');
|
||||
|
||||
describe('AndroidProject', () => {
|
||||
const PROJECT_DIR = 'platforms/android';
|
||||
@@ -30,6 +32,8 @@ describe('AndroidProject', () => {
|
||||
|
||||
AndroidStudioSpy = jasmine.createSpyObj('AndroidStudio', ['isAndroidStudioProject']);
|
||||
AndroidProject.__set__('AndroidStudio', AndroidStudioSpy);
|
||||
|
||||
spyOn(CordovaGradleConfigParserFactory, 'create').and.returnValue(new MockCordovaGradleConfigParser(PROJECT_DIR));
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
@@ -87,26 +91,20 @@ describe('AndroidProject', () => {
|
||||
});
|
||||
|
||||
describe('getPackageName', () => {
|
||||
let AndroidManifestSpy;
|
||||
let AndroidManifestFns;
|
||||
let androidProject;
|
||||
|
||||
beforeEach(() => {
|
||||
AndroidManifestFns = jasmine.createSpyObj('AndroidManifestFns', ['getPackageId']);
|
||||
AndroidManifestSpy = jasmine.createSpy('AndroidManifest').and.returnValue(AndroidManifestFns);
|
||||
AndroidProject.__set__('AndroidManifest', AndroidManifestSpy);
|
||||
|
||||
androidProject = new AndroidProject(PROJECT_DIR);
|
||||
});
|
||||
|
||||
it('should get the package name AndroidManifest', () => {
|
||||
it('should get the package name Cordova Gradle Config file', () => {
|
||||
spyOn(MockCordovaGradleConfigParser.prototype, 'getPackageName');
|
||||
androidProject.getPackageName();
|
||||
expect(AndroidManifestSpy).toHaveBeenCalledWith(path.join(PROJECT_DIR, 'app/src/main/AndroidManifest.xml'));
|
||||
expect(MockCordovaGradleConfigParser.prototype.getPackageName).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return the package name', () => {
|
||||
const packageName = 'io.cordova.unittest';
|
||||
AndroidManifestFns.getPackageId.and.returnValue(packageName);
|
||||
|
||||
expect(androidProject.getPackageName()).toBe(packageName);
|
||||
});
|
||||
|
||||
@@ -24,6 +24,8 @@ const EventEmitter = require('events');
|
||||
|
||||
const Api = require('../../lib/Api');
|
||||
const AndroidProject = require('../../lib/AndroidProject');
|
||||
const check_reqs = require('../../lib/check_reqs');
|
||||
const run_mod = require('../../lib/run');
|
||||
|
||||
const PluginInfo = common.PluginInfo;
|
||||
|
||||
@@ -60,4 +62,19 @@ describe('Api', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('listTargets', () => {
|
||||
let api;
|
||||
|
||||
beforeEach(() => {
|
||||
api = new Api('android', FAKE_PROJECT_DIR, new EventEmitter());
|
||||
spyOn(check_reqs, 'run').and.returnValue(Promise.resolve());
|
||||
});
|
||||
it('should call into lib/run module', () => {
|
||||
spyOn(run_mod, 'runListDevices');
|
||||
return api.listTargets().then(() => {
|
||||
expect(run_mod.runListDevices).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
116
spec/unit/build.spec.js
Normal file
116
spec/unit/build.spec.js
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const rewire = require('rewire');
|
||||
const builders = require('../../lib/builders/builders');
|
||||
|
||||
describe('build', () => {
|
||||
let build;
|
||||
const builder = builders.getBuilder('FakeRootPath');
|
||||
|
||||
beforeEach(() => {
|
||||
build = rewire('../../lib/build');
|
||||
build.__set__({
|
||||
events: jasmine.createSpyObj('eventsSpy', ['emit'])
|
||||
});
|
||||
|
||||
// run needs `this` to behave like an Api instance
|
||||
build.run = build.run.bind({
|
||||
_builder: builder
|
||||
});
|
||||
|
||||
spyOn(builder, 'build').and.returnValue(Promise.resolve({
|
||||
paths: ['fake.apk'],
|
||||
buildtype: 'debug'
|
||||
}));
|
||||
});
|
||||
|
||||
describe('argument parsing', () => {
|
||||
let prepEnvSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
prepEnvSpy = spyOn(builder, 'prepEnv').and.returnValue(Promise.resolve());
|
||||
});
|
||||
|
||||
describe('gradleArg', () => {
|
||||
const baseOptions = {
|
||||
packageType: 'apk',
|
||||
arch: undefined,
|
||||
prepEnv: undefined,
|
||||
buildType: 'debug'
|
||||
};
|
||||
|
||||
it('can parse single gradle argument', async () => {
|
||||
await build.run({
|
||||
argv: [
|
||||
'node',
|
||||
'--gradleArg=--stacktrace'
|
||||
]
|
||||
});
|
||||
|
||||
expect(prepEnvSpy).toHaveBeenCalledWith({
|
||||
...baseOptions,
|
||||
extraArgs: ['--stacktrace']
|
||||
});
|
||||
});
|
||||
|
||||
it('can parse multiple gradle arguments', async () => {
|
||||
await build.run({
|
||||
argv: [
|
||||
'node',
|
||||
'--gradleArg=--stacktrace --info'
|
||||
]
|
||||
});
|
||||
|
||||
expect(prepEnvSpy).toHaveBeenCalledWith({
|
||||
...baseOptions,
|
||||
extraArgs: ['--stacktrace', '--info']
|
||||
});
|
||||
});
|
||||
|
||||
it('can parse multiple gradle arguments with strings', async () => {
|
||||
await build.run({
|
||||
argv: [
|
||||
'node',
|
||||
'--gradleArg=--testArg="hello world"'
|
||||
]
|
||||
});
|
||||
|
||||
expect(prepEnvSpy).toHaveBeenCalledWith({
|
||||
...baseOptions,
|
||||
extraArgs: ['--testArg="hello world"']
|
||||
});
|
||||
});
|
||||
|
||||
it('gradle args will split when necessary', async () => {
|
||||
await build.run({
|
||||
argv: [
|
||||
'node',
|
||||
'--gradleArg=--warning-mode all'
|
||||
]
|
||||
});
|
||||
|
||||
expect(prepEnvSpy).toHaveBeenCalledWith({
|
||||
...baseOptions,
|
||||
extraArgs: ['--warning-mode', 'all']
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -20,8 +20,7 @@
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const rewire = require('rewire');
|
||||
|
||||
const CordovaError = require('cordova-common').CordovaError;
|
||||
const { isWindows } = require('../../../lib/utils');
|
||||
|
||||
describe('ProjectBuilder', () => {
|
||||
const rootDir = '/root';
|
||||
@@ -55,40 +54,40 @@ describe('ProjectBuilder', () => {
|
||||
|
||||
it('should set release argument', () => {
|
||||
const args = builder.getArgs('release', {});
|
||||
expect(args[0]).toBe('cdvBuildRelease');
|
||||
expect(args[args.length - 1]).toBe('cdvBuildRelease');
|
||||
});
|
||||
|
||||
it('should set debug argument', () => {
|
||||
const args = builder.getArgs('debug', {});
|
||||
expect(args[0]).toBe('cdvBuildDebug');
|
||||
expect(args[args.length - 1]).toBe('cdvBuildDebug');
|
||||
});
|
||||
|
||||
it('should set apk release', () => {
|
||||
const args = builder.getArgs('release', {
|
||||
packageType: 'apk'
|
||||
});
|
||||
expect(args[0]).withContext(args).toBe('cdvBuildRelease');
|
||||
expect(args[args.length - 1]).withContext(args).toBe('cdvBuildRelease');
|
||||
});
|
||||
|
||||
it('should set apk debug', () => {
|
||||
const args = builder.getArgs('debug', {
|
||||
packageType: 'apk'
|
||||
});
|
||||
expect(args[0]).withContext(args).toBe('cdvBuildDebug');
|
||||
expect(args[args.length - 1]).withContext(args).toBe('cdvBuildDebug');
|
||||
});
|
||||
|
||||
it('should set bundle release', () => {
|
||||
const args = builder.getArgs('release', {
|
||||
packageType: 'bundle'
|
||||
});
|
||||
expect(args[0]).withContext(args).toBe(':app:bundleRelease');
|
||||
expect(args[args.length - 1]).withContext(args).toBe(':app:bundleRelease');
|
||||
});
|
||||
|
||||
it('should set bundle debug', () => {
|
||||
const args = builder.getArgs('debug', {
|
||||
packageType: 'bundle'
|
||||
});
|
||||
expect(args[0]).withContext(args).toBe(':app:bundleDebug');
|
||||
expect(args[args.length - 1]).withContext(args).toBe(':app:bundleDebug');
|
||||
});
|
||||
|
||||
it('should add architecture if it is passed', () => {
|
||||
@@ -102,14 +101,14 @@ describe('ProjectBuilder', () => {
|
||||
const args = builder.getArgs('clean', {
|
||||
packageType: 'apk'
|
||||
});
|
||||
expect(args[0]).toBe('clean');
|
||||
expect(args[args.length - 1]).toBe('clean');
|
||||
});
|
||||
|
||||
it('should clean bundle', () => {
|
||||
const args = builder.getArgs('clean', {
|
||||
packageType: 'bundle'
|
||||
});
|
||||
expect(args[0]).toBe('clean');
|
||||
expect(args[args.length - 1]).toBe('clean');
|
||||
});
|
||||
|
||||
describe('should accept extra arguments', () => {
|
||||
@@ -130,40 +129,21 @@ describe('ProjectBuilder', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('runGradleWrapper', () => {
|
||||
it('should run the provided gradle command if a gradle wrapper does not already exist', () => {
|
||||
spyOn(fs, 'existsSync').and.returnValue(false);
|
||||
builder.runGradleWrapper('/my/sweet/gradle');
|
||||
expect(execaSpy).toHaveBeenCalledWith('/my/sweet/gradle', jasmine.any(Array), jasmine.any(Object));
|
||||
describe('installGradleWrapper', () => {
|
||||
beforeEach(() => {
|
||||
execaSpy.and.resolveTo();
|
||||
});
|
||||
|
||||
it('should do nothing if a gradle wrapper exists in the project directory', () => {
|
||||
spyOn(fs, 'existsSync').and.returnValue(true);
|
||||
builder.runGradleWrapper('/my/sweet/gradle');
|
||||
expect(execaSpy).not.toHaveBeenCalledWith('/my/sweet/gradle', jasmine.any(Array), jasmine.any(Object));
|
||||
});
|
||||
});
|
||||
|
||||
describe('extractRealProjectNameFromManifest', () => {
|
||||
it('should get the project name from the Android Manifest', () => {
|
||||
const projectName = 'unittestproject';
|
||||
const projectId = `io.cordova.${projectName}`;
|
||||
const manifest = `<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="${projectId}"></manifest>`;
|
||||
|
||||
spyOn(fs, 'readFileSync').and.returnValue(manifest);
|
||||
|
||||
expect(builder.extractRealProjectNameFromManifest()).toBe(projectName);
|
||||
it('should run gradle wrapper 8.7', async () => {
|
||||
await builder.installGradleWrapper('8.7');
|
||||
expect(execaSpy).toHaveBeenCalledWith('gradle', ['-p', path.normalize('/root/tools'), 'wrapper', '--gradle-version', '8.7'], jasmine.any(Object));
|
||||
});
|
||||
|
||||
it('should throw an error if there is no package in the Android manifest', () => {
|
||||
const manifest = `<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"></manifest>`;
|
||||
|
||||
spyOn(fs, 'readFileSync').and.returnValue(manifest);
|
||||
|
||||
expect(() => builder.extractRealProjectNameFromManifest()).toThrow(jasmine.any(CordovaError));
|
||||
it('CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL should override gradle version', async () => {
|
||||
process.env.CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL = 'https://dist.local';
|
||||
await builder.installGradleWrapper('8.7');
|
||||
delete process.env.CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL;
|
||||
expect(execaSpy).toHaveBeenCalledWith('gradle', ['-p', path.normalize('/root/tools'), 'wrapper', '--gradle-distribution-url', 'https://dist.local'], jasmine.any(Object));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -196,12 +176,18 @@ describe('ProjectBuilder', () => {
|
||||
|
||||
builder.build({});
|
||||
|
||||
expect(execaSpy).toHaveBeenCalledWith(path.join(rootDir, 'gradlew'), testArgs, jasmine.anything());
|
||||
let gradle = path.join(rootDir, 'tools', 'gradlew');
|
||||
if (isWindows()) {
|
||||
gradle += '.bat';
|
||||
}
|
||||
|
||||
expect(execaSpy).toHaveBeenCalledWith(gradle, testArgs, jasmine.anything());
|
||||
});
|
||||
|
||||
it('should reject if the spawn fails', () => {
|
||||
const errorMessage = 'Test error';
|
||||
execaSpy.and.rejectWith(new Error(errorMessage));
|
||||
builder.getArgs.and.returnValue([]);
|
||||
|
||||
return builder.build({}).then(
|
||||
() => fail('Unexpectedly resolved'),
|
||||
@@ -218,6 +204,7 @@ describe('ProjectBuilder', () => {
|
||||
ProjectBuilder.__set__('check_reqs', checkReqsSpy);
|
||||
checkReqsSpy.check_android_target.and.resolveTo();
|
||||
execaSpy.and.rejectWith(testError);
|
||||
builder.getArgs.and.returnValue([]);
|
||||
|
||||
return builder.build({}).then(
|
||||
() => fail('Unexpectedly resolved'),
|
||||
@@ -251,8 +238,13 @@ describe('ProjectBuilder', () => {
|
||||
const gradleArgs = ['test', 'args', '-f'];
|
||||
builder.getArgs.and.returnValue(gradleArgs);
|
||||
|
||||
let gradle = path.join(rootDir, 'tools', 'gradlew');
|
||||
if (isWindows()) {
|
||||
gradle += '.bat';
|
||||
}
|
||||
|
||||
return builder.clean(opts).then(() => {
|
||||
expect(execaSpy).toHaveBeenCalledWith(path.join(rootDir, 'gradlew'), gradleArgs, jasmine.anything());
|
||||
expect(execaSpy).toHaveBeenCalledWith(gradle, gradleArgs, jasmine.anything());
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ describe('check_reqs', function () {
|
||||
});
|
||||
|
||||
describe('check_android', function () {
|
||||
describe('find and set ANDROID_HOME when ANDROID_HOME and ANDROID_SDK_ROOT is not set', function () {
|
||||
describe('find and set ANDROID_HOME when neither ANDROID_HOME nor ANDROID_SDK_ROOT is set', function () {
|
||||
beforeEach(function () {
|
||||
delete process.env.ANDROID_HOME;
|
||||
delete process.env.ANDROID_SDK_ROOT;
|
||||
@@ -150,32 +150,24 @@ describe('check_reqs', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('ANDROID_SDK_ROOT environment variable detection', () => {
|
||||
describe('ANDROID_HOME environment variable detection', () => {
|
||||
beforeEach(() => {
|
||||
delete process.env.ANDROID_SDK_ROOT;
|
||||
delete process.env.ANDROID_HOME;
|
||||
delete process.env.ANDROID_SDK_ROOT;
|
||||
check_reqs.__set__('forgivingWhichSync', jasmine.createSpy().and.returnValue(''));
|
||||
});
|
||||
|
||||
const expectedAndroidSdkPath = path.sep + 'android' + path.sep + 'sdk';
|
||||
const expectedAndroidRootSdkPath = path.sep + 'android' + path.sep + 'sdk' + path.sep + 'root';
|
||||
|
||||
it('should error if neither ANDROID_SDK_ROOT or ANDROID_HOME is defined', () => {
|
||||
it('should error if neither ANDROID_HOME nor ANDROID_SDK_ROOT is defined', () => {
|
||||
spyOn(fs, 'existsSync').and.returnValue(true);
|
||||
return check_reqs.check_android().catch((error) => {
|
||||
expect(error.toString()).toContain('Failed to find \'ANDROID_SDK_ROOT\' environment variable.');
|
||||
expect(error.toString()).toContain('Failed to find \'ANDROID_HOME\' environment variable.');
|
||||
});
|
||||
});
|
||||
|
||||
it('should use ANDROID_SDK_ROOT if defined', () => {
|
||||
spyOn(fs, 'existsSync').and.returnValue(true);
|
||||
process.env.ANDROID_SDK_ROOT = path.normalize('/android/sdk');
|
||||
return check_reqs.check_android().then(() => {
|
||||
expect(process.env.ANDROID_SDK_ROOT).toContain(expectedAndroidSdkPath);
|
||||
});
|
||||
});
|
||||
|
||||
it('should use ANDROID_HOME if defined and ANDROID_SDK_ROOT is not defined', () => {
|
||||
it('should use ANDROID_HOME if defined', () => {
|
||||
spyOn(fs, 'existsSync').and.returnValue(true);
|
||||
process.env.ANDROID_HOME = path.normalize('/android/sdk');
|
||||
return check_reqs.check_android().then(() => {
|
||||
@@ -183,15 +175,23 @@ describe('check_reqs', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should use ANDROID_HOME if defined and ANDROID_SDK_ROOT is defined', () => {
|
||||
it('should use ANDROID_SDK_ROOT if defined and ANDROID_HOME is not defined', () => {
|
||||
spyOn(fs, 'existsSync').and.returnValue(true);
|
||||
process.env.ANDROID_SDK_ROOT = path.normalize('/android/sdk/root');
|
||||
process.env.ANDROID_HOME = path.normalize('/android/sdk');
|
||||
return check_reqs.check_android().then(() => {
|
||||
expect(process.env.ANDROID_SDK_ROOT).toContain(expectedAndroidRootSdkPath);
|
||||
});
|
||||
});
|
||||
|
||||
it('should use ANDROID_HOME if defined and ANDROID_SDK_ROOT is defined', () => {
|
||||
spyOn(fs, 'existsSync').and.returnValue(true);
|
||||
process.env.ANDROID_HOME = path.normalize('/android/sdk');
|
||||
process.env.ANDROID_SDK_ROOT = path.normalize('/android/sdk/root');
|
||||
return check_reqs.check_android().then(() => {
|
||||
expect(process.env.ANDROID_HOME).toContain(expectedAndroidSdkPath);
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw if ANDROID_HOME points to an invalid path', () => {
|
||||
process.env.ANDROID_HOME = path.normalize('/android/sdk');
|
||||
return check_reqs.check_android().catch((error) => {
|
||||
@@ -219,30 +219,30 @@ describe('check_reqs', function () {
|
||||
describe('check_gradle', () => {
|
||||
describe('environment variable checks', () => {
|
||||
beforeEach(() => {
|
||||
delete process.env.ANDROID_SDK_ROOT;
|
||||
delete process.env.ANDROID_HOME;
|
||||
delete process.env.ANDROID_SDK_ROOT;
|
||||
spyOn(check_reqs, 'get_gradle_wrapper').and.callFake(() => {
|
||||
return path.normalize((process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT) + '/bin/gradle');
|
||||
});
|
||||
});
|
||||
|
||||
it('with ANDROID_SDK_ROOT / without ANDROID_HOME', async () => {
|
||||
it('with ANDROID_HOME / without ANDROID_SDK_ROOT', async () => {
|
||||
process.env.ANDROID_HOME = path.normalize('/android/sdk/home');
|
||||
await expectAsync(check_reqs.check_gradle()).toBeResolvedTo(path.normalize('/android/sdk/home/bin/gradle'));
|
||||
});
|
||||
|
||||
it('without ANDROID_HOME / with ANDROID_SDK_ROOT', async () => {
|
||||
process.env.ANDROID_SDK_ROOT = path.normalize('/android/sdk/root');
|
||||
await expectAsync(check_reqs.check_gradle()).toBeResolvedTo(path.normalize('/android/sdk/root/bin/gradle'));
|
||||
});
|
||||
|
||||
it('with ANDROID_SDK_ROOT / with ANDROID_HOME', async () => {
|
||||
it('with ANDROID_HOME / with ANDROID_SDK_ROOT', async () => {
|
||||
process.env.ANDROID_HOME = path.normalize('/android/sdk/home');
|
||||
process.env.ANDROID_SDK_ROOT = path.normalize('/android/sdk/root');
|
||||
process.env.ANDROID_HOME = path.normalize('/android/sdk/home');
|
||||
await expectAsync(check_reqs.check_gradle()).toBeResolvedTo(path.normalize('/android/sdk/home/bin/gradle'));
|
||||
});
|
||||
|
||||
it('without ANDROID_SDK_ROOT / with ANDROID_HOME', async () => {
|
||||
process.env.ANDROID_HOME = path.normalize('/android/sdk/home');
|
||||
await expectAsync(check_reqs.check_gradle()).toBeResolvedTo(path.normalize('/android/sdk/home/bin/gradle'));
|
||||
});
|
||||
|
||||
it('without ANDROID_SDK_ROOT / without ANDROID_HOME', () => {
|
||||
it('without ANDROID_HOME / without ANDROID_SDK_ROOT', () => {
|
||||
return check_reqs.check_gradle().catch((error) => {
|
||||
expect(error.toString()).toContain('Could not find gradle wrapper within Android SDK. Could not find Android SDK directory.');
|
||||
});
|
||||
@@ -250,7 +250,7 @@ describe('check_reqs', function () {
|
||||
});
|
||||
|
||||
it('should error if sdk is installed but no gradle found', () => {
|
||||
process.env.ANDROID_SDK_ROOT = path.normalize('/android/sdk');
|
||||
process.env.ANDROID_HOME = path.normalize('/android/sdk');
|
||||
spyOn(check_reqs, 'get_gradle_wrapper').and.callFake(() => {
|
||||
return '';
|
||||
});
|
||||
|
||||
@@ -23,8 +23,16 @@ const create = rewire('../../lib/create');
|
||||
const check_reqs = require('../../lib/check_reqs');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const MockCordovaGradleConfigParser = require('./mocks/config/MockCordovaGradleConfigParser');
|
||||
const CordovaGradleConfigParserFactory = require('../../lib/config/CordovaGradleConfigParserFactory');
|
||||
|
||||
describe('create', function () {
|
||||
const PROJECT_DIR = 'platforms/android';
|
||||
|
||||
beforeAll(() => {
|
||||
spyOn(CordovaGradleConfigParserFactory, 'create').and.returnValue(new MockCordovaGradleConfigParser(PROJECT_DIR));
|
||||
});
|
||||
|
||||
describe('validatePackageName helper method', function () {
|
||||
describe('happy path (valid package names)', function () {
|
||||
const valid = [
|
||||
|
||||
32
spec/unit/mocks/config/MockCordovaGradleConfigParser.js
Normal file
32
spec/unit/mocks/config/MockCordovaGradleConfigParser.js
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const CordovaGradleConfigParser = require('../../../../lib/config/CordovaGradleConfigParser');
|
||||
|
||||
module.exports = class MockCordoCordovaGradleConfigParservaGradleConfigParser extends CordovaGradleConfigParser {
|
||||
_readConfig (configPath) {
|
||||
return {
|
||||
PACKAGE_NAMESPACE: 'io.cordova.unittest'
|
||||
};
|
||||
}
|
||||
|
||||
write () {
|
||||
// Pretend write :)
|
||||
}
|
||||
};
|
||||
@@ -32,6 +32,9 @@ const android_studio_project = path.join(__dirname, '../../fixtures/android_stud
|
||||
const PluginInfo = require('cordova-common').PluginInfo;
|
||||
const AndroidProject = require('../../../lib/AndroidProject');
|
||||
|
||||
const MockCordovaGradleConfigParser = require('../mocks/config/MockCordovaGradleConfigParser');
|
||||
const CordovaGradleConfigParserFactory = require('../../../lib/config/CordovaGradleConfigParserFactory');
|
||||
|
||||
const dummyPluginInfo = new PluginInfo(dummyplugin);
|
||||
const valid_source = dummyPluginInfo.getSourceFiles('android');
|
||||
const valid_resources = dummyPluginInfo.getResourceFiles('android');
|
||||
@@ -41,6 +44,12 @@ const faultyPluginInfo = new PluginInfo(faultyplugin);
|
||||
const invalid_source = faultyPluginInfo.getSourceFiles('android');
|
||||
|
||||
describe('android project handler', function () {
|
||||
const PROJECT_DIR = 'platforms/android';
|
||||
|
||||
beforeAll(() => {
|
||||
spyOn(CordovaGradleConfigParserFactory, 'create').and.returnValue(new MockCordovaGradleConfigParser(PROJECT_DIR));
|
||||
});
|
||||
|
||||
describe('installation', function () {
|
||||
const copyFileOrig = common.__get__('copyFile');
|
||||
const copyFileSpy = jasmine.createSpy('copyFile');
|
||||
|
||||
@@ -23,6 +23,8 @@ const CordovaError = require('cordova-common').CordovaError;
|
||||
const GradlePropertiesParser = require('../../lib/config/GradlePropertiesParser');
|
||||
const utils = require('../../lib/utils');
|
||||
const et = require('elementtree');
|
||||
const MockCordovaGradleConfigParser = require('./mocks/config/MockCordovaGradleConfigParser');
|
||||
const CordovaGradleConfigParserFactory = require('../../lib/config/CordovaGradleConfigParserFactory');
|
||||
|
||||
const PATH_RESOURCE = path.join('platforms', 'android', 'app', 'src', 'main', 'res');
|
||||
|
||||
@@ -51,8 +53,10 @@ function createResourceMap (target) {
|
||||
if (!target || target === 'ic_launcher.png') resources[path.join(PATH_RESOURCE, mipmap, 'ic_launcher.png')] = null;
|
||||
if (!target || target === 'ic_launcher_foreground.png') resources[path.join(PATH_RESOURCE, mipmap, 'ic_launcher_foreground.png')] = null;
|
||||
if (!target || target === 'ic_launcher_background.png') resources[path.join(PATH_RESOURCE, mipmap, 'ic_launcher_background.png')] = null;
|
||||
if (!target || target === 'ic_launcher_background.png') resources[path.join(PATH_RESOURCE, mipmap, 'ic_launcher_monochrome.png')] = null;
|
||||
if (!target || target === 'ic_launcher_foreground.xml') resources[path.join(PATH_RESOURCE, mipmap, 'ic_launcher_foreground.xml')] = null;
|
||||
if (!target || target === 'ic_launcher_background.xml') resources[path.join(PATH_RESOURCE, mipmap, 'ic_launcher_background.xml')] = null;
|
||||
if (!target || target === 'ic_launcher_background.xml') resources[path.join(PATH_RESOURCE, mipmap, 'ic_launcher_monochrome.xml')] = null;
|
||||
|
||||
if (
|
||||
!mipmap.includes('-v26') &&
|
||||
@@ -91,6 +95,12 @@ describe('prepare', () => {
|
||||
let emitSpy;
|
||||
let updatePathsSpy;
|
||||
|
||||
const PROJECT_DIR = 'platforms/android';
|
||||
|
||||
beforeAll(() => {
|
||||
spyOn(CordovaGradleConfigParserFactory, 'create').and.returnValue(new MockCordovaGradleConfigParser(PROJECT_DIR));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
prepare = rewire('../../lib/prepare');
|
||||
|
||||
@@ -136,10 +146,14 @@ describe('prepare', () => {
|
||||
return createResourceMap('ic_launcher_foreground.png');
|
||||
} else if (resourceName.includes('ic_launcher_background.png')) {
|
||||
return createResourceMap('ic_launcher_background.png');
|
||||
} else if (resourceName.includes('ic_launcher_monochrome.png')) {
|
||||
return createResourceMap('ic_launcher_monochrome.png');
|
||||
} else if (resourceName.includes('ic_launcher_foreground.xml')) {
|
||||
return createResourceMap('ic_launcher_foreground.xml');
|
||||
} else if (resourceName.includes('ic_launcher_background.xml')) {
|
||||
return createResourceMap('ic_launcher_background.xml');
|
||||
} else if (resourceName.includes('ic_launcher_monochrome.xml')) {
|
||||
return createResourceMap('ic_launcher_monochrome.xml');
|
||||
} else if (resourceName.includes('ic_launcher.xml')) {
|
||||
return createResourceMap('ic_launcher.xml');
|
||||
}
|
||||
@@ -305,7 +319,9 @@ describe('prepare', () => {
|
||||
return [mockGetIconItem({
|
||||
density: 'mdpi',
|
||||
background: 'res/icon/android/mdpi-background.png',
|
||||
foreground: 'res/icon/android/mdpi-foreground.xml'
|
||||
foreground: 'res/icon/android/mdpi-foreground.xml',
|
||||
monochrome: 'res/icon/android/mdpi-monochrome.png'
|
||||
|
||||
})];
|
||||
};
|
||||
|
||||
@@ -343,7 +359,8 @@ describe('prepare', () => {
|
||||
return [mockGetIconItem({
|
||||
density: 'mdpi',
|
||||
background: 'res/icon/android/mdpi-background.png',
|
||||
foreground: 'res/icon/android/mdpi-foreground.png'
|
||||
foreground: 'res/icon/android/mdpi-foreground.png',
|
||||
monochrome: 'res/icon/android/mdpi-monochrome.png'
|
||||
})];
|
||||
};
|
||||
|
||||
@@ -352,6 +369,7 @@ describe('prepare', () => {
|
||||
const phaseOneModification = {};
|
||||
phaseOneModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_foreground.png')] = 'res/icon/android/mdpi-foreground.png';
|
||||
phaseOneModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_background.png')] = 'res/icon/android/mdpi-background.png';
|
||||
phaseOneModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_monochrome.png')] = 'res/icon/android/mdpi-monochrome.png';
|
||||
const phaseOneUpdatedIconsForAdaptive = Object.assign({}, resourceMap, phaseOneModification);
|
||||
|
||||
updateIconResourceForAdaptiveSpy = jasmine.createSpy('updateIconResourceForAdaptiveSpy');
|
||||
@@ -363,6 +381,7 @@ describe('prepare', () => {
|
||||
const phaseTwoModification = {};
|
||||
phaseTwoModification[path.join(PATH_RESOURCE, 'mipmap-mdpi', 'ic_launcher.png')] = 'res/icon/android/mdpi-foreground.png';
|
||||
phaseTwoModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_background.png')] = 'res/icon/android/mdpi-background.png';
|
||||
phaseTwoModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_monochrome.png')] = 'res/icon/android/mdpi-monochrome.png';
|
||||
const phaseTwoUpdatedIconsForLegacy = Object.assign({}, phaseOneUpdatedIconsForAdaptive, phaseTwoModification);
|
||||
|
||||
updateIconResourceForLegacySpy = jasmine.createSpy('updateIconResourceForLegacySpy');
|
||||
@@ -400,7 +419,8 @@ describe('prepare', () => {
|
||||
density: 'mdpi',
|
||||
src: 'res/icon/android/mdpi-icon.png',
|
||||
background: 'res/icon/android/mdpi-background.png',
|
||||
foreground: 'res/icon/android/mdpi-foreground.png'
|
||||
foreground: 'res/icon/android/mdpi-foreground.png',
|
||||
monochrome: 'res/icon/android/mdpi-monochrome.png'
|
||||
})];
|
||||
};
|
||||
|
||||
@@ -409,6 +429,7 @@ describe('prepare', () => {
|
||||
const phaseOneModification = {};
|
||||
phaseOneModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_foreground.png')] = 'res/icon/android/mdpi-foreground.png';
|
||||
phaseOneModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_background.png')] = 'res/icon/android/mdpi-background.png';
|
||||
phaseOneModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_monochrome.png')] = 'res/icon/android/mdpi-monochrome.png';
|
||||
const phaseOneUpdatedIconsForAdaptive = Object.assign({}, resourceMap, phaseOneModification);
|
||||
|
||||
updateIconResourceForAdaptiveSpy = jasmine.createSpy('updateIconResourceForAdaptiveSpy');
|
||||
@@ -420,6 +441,7 @@ describe('prepare', () => {
|
||||
const phaseTwoModification = {};
|
||||
phaseTwoModification[path.join(PATH_RESOURCE, 'mipmap-mdpi', 'ic_launcher.png')] = 'res/icon/android/mdpi-foreground.png';
|
||||
phaseTwoModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_background.png')] = 'res/icon/android/mdpi-background.png';
|
||||
phaseTwoModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_monochrome.png')] = 'res/icon/android/mdpi-monochrome.png';
|
||||
const phaseTwoUpdatedIconsForLegacy = Object.assign({}, phaseOneUpdatedIconsForAdaptive, phaseTwoModification);
|
||||
|
||||
updateIconResourceForLegacySpy = jasmine.createSpy('updateIconResourceForLegacySpy');
|
||||
@@ -511,13 +533,15 @@ describe('prepare', () => {
|
||||
const ldpi = mockGetIconItem({
|
||||
density: 'ldpi',
|
||||
background: 'res/icon/android/ldpi-background.png',
|
||||
foreground: 'res/icon/android/ldpi-foreground.png'
|
||||
foreground: 'res/icon/android/ldpi-foreground.png',
|
||||
monochrome: 'res/icon/android/ldpi-monochrome.png'
|
||||
});
|
||||
|
||||
const mdpi = mockGetIconItem({
|
||||
density: 'mdpi',
|
||||
background: 'res/icon/android/mdpi-background.png',
|
||||
foreground: 'res/icon/android/mdpi-foreground.png'
|
||||
foreground: 'res/icon/android/mdpi-foreground.png',
|
||||
monochrome: 'res/icon/android/mdpi-monochrome.png'
|
||||
});
|
||||
|
||||
const icons = [ldpi, mdpi];
|
||||
@@ -606,41 +630,106 @@ describe('prepare', () => {
|
||||
let preparedIcons;
|
||||
let resourceMap;
|
||||
|
||||
beforeEach(function () {
|
||||
// Mocked Data
|
||||
platformResourcesDir = PATH_RESOURCE;
|
||||
preparedIcons = {
|
||||
android_icons: {
|
||||
mdpi: mockGetIconItem({
|
||||
density: 'mdpi',
|
||||
background: 'res/icon/android/mdpi-background.png',
|
||||
foreground: 'res/icon/android/mdpi-foreground.png'
|
||||
})
|
||||
},
|
||||
default_icon: undefined
|
||||
};
|
||||
describe('without monochrome', () => {
|
||||
beforeEach(function () {
|
||||
// Mocked Data
|
||||
platformResourcesDir = PATH_RESOURCE;
|
||||
preparedIcons = {
|
||||
android_icons: {
|
||||
mdpi: mockGetIconItem({
|
||||
density: 'mdpi',
|
||||
background: 'res/icon/android/mdpi-background.png',
|
||||
foreground: 'res/icon/android/mdpi-foreground.png'
|
||||
})
|
||||
},
|
||||
default_icon: undefined
|
||||
};
|
||||
|
||||
resourceMap = createResourceMap();
|
||||
resourceMap = createResourceMap();
|
||||
|
||||
fsWriteFileSyncSpy = jasmine.createSpy('writeFileSync');
|
||||
prepare.__set__('fs', {
|
||||
writeFileSync: fsWriteFileSyncSpy
|
||||
fsWriteFileSyncSpy = jasmine.createSpy('writeFileSync');
|
||||
prepare.__set__('fs', {
|
||||
writeFileSync: fsWriteFileSyncSpy
|
||||
});
|
||||
});
|
||||
|
||||
it('Test#001 : Should update resource map with prepared icons.', function () {
|
||||
// Get method for testing
|
||||
const updateIconResourceForAdaptive = prepare.__get__('updateIconResourceForAdaptive');
|
||||
|
||||
// Run Test
|
||||
const expectedModification = {};
|
||||
expectedModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_background.png')] = 'res/icon/android/mdpi-background.png';
|
||||
expectedModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_foreground.png')] = 'res/icon/android/mdpi-foreground.png';
|
||||
|
||||
const expected = Object.assign({}, resourceMap, expectedModification);
|
||||
const actual = updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
it('Test#001 : Should update resource map with prepared icons.', function () {
|
||||
// Get method for testing
|
||||
const updateIconResourceForAdaptive = prepare.__get__('updateIconResourceForAdaptive');
|
||||
describe('with monochrome', () => {
|
||||
beforeEach(function () {
|
||||
// Mocked Data
|
||||
platformResourcesDir = PATH_RESOURCE;
|
||||
preparedIcons = {
|
||||
android_icons: {
|
||||
mdpi: mockGetIconItem({
|
||||
density: 'mdpi',
|
||||
background: 'res/icon/android/mdpi-background.png',
|
||||
foreground: 'res/icon/android/mdpi-foreground.png',
|
||||
monochrome: 'res/icon/android/mdpi-monochrome.png'
|
||||
})
|
||||
},
|
||||
default_icon: undefined
|
||||
};
|
||||
|
||||
// Run Test
|
||||
const expectedModification = {};
|
||||
expectedModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_background.png')] = 'res/icon/android/mdpi-background.png';
|
||||
expectedModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_foreground.png')] = 'res/icon/android/mdpi-foreground.png';
|
||||
resourceMap = createResourceMap();
|
||||
|
||||
const expected = Object.assign({}, resourceMap, expectedModification);
|
||||
const actual = updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
|
||||
fsWriteFileSyncSpy = jasmine.createSpy('writeFileSync');
|
||||
prepare.__set__('fs', {
|
||||
writeFileSync: fsWriteFileSyncSpy
|
||||
});
|
||||
});
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
it('Test#002 : Should update resource map with prepared icons.', function () {
|
||||
// Get method for testing
|
||||
const updateIconResourceForAdaptive = prepare.__get__('updateIconResourceForAdaptive');
|
||||
|
||||
// Run Test
|
||||
const expectedModification = {};
|
||||
expectedModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_background.png')] = 'res/icon/android/mdpi-background.png';
|
||||
expectedModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_foreground.png')] = 'res/icon/android/mdpi-foreground.png';
|
||||
expectedModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_monochrome.png')] = 'res/icon/android/mdpi-monochrome.png';
|
||||
|
||||
const expected = Object.assign({}, resourceMap, expectedModification);
|
||||
const actual = updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('Test#003 : should emit if monochrome is supplied without adaptive background', () => {
|
||||
const updateIconResourceForAdaptive = prepare.__get__('updateIconResourceForAdaptive');
|
||||
|
||||
preparedIcons.android_icons.mdpi.background = undefined;
|
||||
updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
|
||||
|
||||
const actualEmitArgs = emitSpy.calls.mostRecent().args;
|
||||
expect(actualEmitArgs[0]).toBe('warn');
|
||||
expect(actualEmitArgs[1]).toMatch(/Monochrome icon found but without adaptive properties./g);
|
||||
});
|
||||
|
||||
it('Test#004 : should emit if monochrome is supplied without adaptive foreground', () => {
|
||||
const updateIconResourceForAdaptive = prepare.__get__('updateIconResourceForAdaptive');
|
||||
|
||||
preparedIcons.android_icons.mdpi.foreground = undefined;
|
||||
updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
|
||||
|
||||
const actualEmitArgs = emitSpy.calls.mostRecent().args;
|
||||
expect(actualEmitArgs[0]).toBe('warn');
|
||||
expect(actualEmitArgs[1]).toMatch(/Monochrome icon found but without adaptive properties./g);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -668,7 +757,8 @@ describe('prepare', () => {
|
||||
const icons = [mockGetIconItem({
|
||||
density: 'mdpi',
|
||||
background: 'res/icon/android/mdpi-background.png',
|
||||
foreground: 'res/icon/android/mdpi-foreground.png'
|
||||
foreground: 'res/icon/android/mdpi-foreground.png',
|
||||
monochrome: 'res/icon/android/mdpi-monochrome.png'
|
||||
})];
|
||||
const projectRoot = '/mock';
|
||||
const projectConfig = {
|
||||
@@ -893,9 +983,7 @@ describe('prepare', () => {
|
||||
}),
|
||||
setVersionName: jasmine.createSpy('setVersionName').and.returnValue({
|
||||
setVersionCode: jasmine.createSpy('setVersionCode').and.returnValue({
|
||||
setPackageId: jasmine.createSpy('setPackageId').and.returnValue({
|
||||
write: jasmine.createSpy('write')
|
||||
})
|
||||
write: jasmine.createSpy('write')
|
||||
})
|
||||
})
|
||||
}));
|
||||
|
||||
@@ -19,10 +19,19 @@
|
||||
|
||||
const rewire = require('rewire');
|
||||
const builders = require('../../lib/builders/builders');
|
||||
const MockCordovaGradleConfigParser = require('./mocks/config/MockCordovaGradleConfigParser');
|
||||
const CordovaGradleConfigParser = require('../../lib/config/CordovaGradleConfigParser');
|
||||
const CordovaGradleConfigParserFactory = require('../../lib/config/CordovaGradleConfigParserFactory');
|
||||
|
||||
describe('run', () => {
|
||||
let run;
|
||||
|
||||
const PROJECT_DIR = 'platforms/android';
|
||||
|
||||
beforeAll(() => {
|
||||
spyOn(CordovaGradleConfigParserFactory, 'create').and.returnValue(new MockCordovaGradleConfigParser(PROJECT_DIR));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
run = rewire('../../lib/run');
|
||||
run.__set__({
|
||||
@@ -84,7 +93,8 @@ describe('run', () => {
|
||||
buildResults: {
|
||||
buildType: 'debug',
|
||||
apkPaths: ['fake.apk']
|
||||
}
|
||||
},
|
||||
cordovaGradleConfigParser: jasmine.any(CordovaGradleConfigParser)
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -96,4 +106,32 @@ describe('run', () => {
|
||||
.toBeRejectedWithError(/Package type "bundle" is not supported/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('--list option', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(run, 'listDevices').and.returnValue(Promise.resolve());
|
||||
spyOn(run, 'listEmulators').and.returnValue(Promise.resolve());
|
||||
});
|
||||
|
||||
it('should delegate to "listDevices" when the "runListDevices" method options param contains "options.device".', () => {
|
||||
return run.runListDevices({ options: { device: true } }).then(() => {
|
||||
expect(run.listDevices).toHaveBeenCalled();
|
||||
expect(run.listEmulators).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should delegate to "listDevices" when the "runListDevices" method options param contains "options.emulator".', () => {
|
||||
return run.runListDevices({ options: { emulator: true } }).then(() => {
|
||||
expect(run.listDevices).not.toHaveBeenCalled();
|
||||
expect(run.listEmulators).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should delegate to both "listEmulators" and "listDevices" when the "runListDevices" method does not contain "options.device" or "options.emulator".', () => {
|
||||
return run.runListDevices({ options: {} }).then(() => {
|
||||
expect(run.listDevices).toHaveBeenCalled();
|
||||
expect(run.listEmulators).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,10 +19,18 @@
|
||||
|
||||
const rewire = require('rewire');
|
||||
const { CordovaError } = require('cordova-common');
|
||||
const MockCordovaGradleConfigParser = require('./mocks/config/MockCordovaGradleConfigParser');
|
||||
const CordovaGradleConfigParserFactory = require('../../lib/config/CordovaGradleConfigParserFactory');
|
||||
|
||||
describe('target', () => {
|
||||
let target;
|
||||
|
||||
const PROJECT_DIR = 'platforms/android';
|
||||
|
||||
beforeAll(() => {
|
||||
spyOn(CordovaGradleConfigParserFactory, 'create').and.returnValue(new MockCordovaGradleConfigParser(PROJECT_DIR));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
target = rewire('../../lib/target');
|
||||
});
|
||||
@@ -228,14 +236,18 @@ describe('target', () => {
|
||||
describe('install', () => {
|
||||
let AdbSpy;
|
||||
let buildSpy;
|
||||
let installTarget, manifest, appSpec;
|
||||
let installTarget, manifest, cordovaGradleConfigParser, appSpec;
|
||||
|
||||
beforeEach(() => {
|
||||
installTarget = { id: 'emulator-5556', type: 'emulator', arch: 'atari' };
|
||||
|
||||
manifest = jasmine.createSpyObj('manifestStub', ['getPackageId', 'getActivity']);
|
||||
manifest = jasmine.createSpyObj('manifestStub', ['getActivity']);
|
||||
manifest.getActivity.and.returnValue(jasmine.createSpyObj('Activity', ['getName']));
|
||||
appSpec = { manifest, buildResults: {} };
|
||||
|
||||
cordovaGradleConfigParser = jasmine.createSpyObj('cordovaGradleConfigParserStub', ['getPackageName']);
|
||||
cordovaGradleConfigParser.getPackageName.and.returnValue('unittestapp');
|
||||
|
||||
appSpec = { manifest, buildResults: {}, cordovaGradleConfigParser };
|
||||
|
||||
buildSpy = jasmine.createSpyObj('build', ['findBestApkForArchitecture']);
|
||||
target.__set__('build', buildSpy);
|
||||
@@ -267,7 +279,7 @@ describe('target', () => {
|
||||
const apkPath = 'my/apk/path/app.apk';
|
||||
buildSpy.findBestApkForArchitecture.and.returnValue(apkPath);
|
||||
|
||||
return target.install(installTarget, { manifest, buildResults }).then(() => {
|
||||
return target.install(installTarget, { manifest, buildResults, cordovaGradleConfigParser: CordovaGradleConfigParserFactory.create(PROJECT_DIR) }).then(() => {
|
||||
expect(buildSpy.findBestApkForArchitecture).toHaveBeenCalledWith(buildResults, installTarget.arch);
|
||||
|
||||
expect(AdbSpy.install.calls.argsFor(0)[1]).toBe(apkPath);
|
||||
@@ -308,7 +320,7 @@ describe('target', () => {
|
||||
it('should start the newly installed app on the device', () => {
|
||||
const packageId = 'unittestapp';
|
||||
const activityName = 'TestActivity';
|
||||
manifest.getPackageId.and.returnValue(packageId);
|
||||
cordovaGradleConfigParser.getPackageName.and.returnValue(packageId);
|
||||
manifest.getActivity().getName.and.returnValue(activityName);
|
||||
|
||||
return target.install(installTarget, appSpec).then(() => {
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
under the License.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="__PACKAGE__" android:versionName="1.0" android:versionCode="1" android:hardwareAccelerated="true">
|
||||
android:versionName="1.0"
|
||||
android:versionCode="1"
|
||||
android:hardwareAccelerated="true">
|
||||
<supports-screens
|
||||
android:largeScreens="true"
|
||||
android:normalScreens="true"
|
||||
@@ -44,5 +46,14 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.cdv.core.file.provider" android:exported="false" android:grantUriPermissions="true">
|
||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/cdv_core_file_provider_paths" />
|
||||
</provider>
|
||||
</application>
|
||||
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.media.action.IMAGE_CAPTURE" />
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
||||
|
||||
@@ -21,7 +21,11 @@ apply plugin: 'com.android.application'
|
||||
|
||||
if (cordovaConfig.IS_GRADLE_PLUGIN_KOTLIN_ENABLED) {
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
if(!cdvHelpers.isVersionGreaterThanEqual(cordovaConfig.KOTLIN_VERSION, '1.8.0')) {
|
||||
println "Kotlin version < 1.8.0 detected. Applying kotlin-android-extensions plugin."
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
}
|
||||
}
|
||||
|
||||
buildscript {
|
||||
@@ -179,9 +183,15 @@ task cdvPrintProps {
|
||||
}
|
||||
|
||||
android {
|
||||
namespace cordovaConfig.PACKAGE_NAMESPACE
|
||||
|
||||
buildFeatures {
|
||||
buildConfig true
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
versionCode cdvVersionCode ?: new BigInteger("" + privateHelpers.extractIntFromManifest("versionCode"))
|
||||
applicationId privateHelpers.extractStringFromManifest("package")
|
||||
applicationId cordovaConfig.PACKAGE_NAMESPACE
|
||||
|
||||
minSdkVersion cordovaConfig.MIN_SDK_VERSION
|
||||
if (cordovaConfig.MAX_SDK_VERSION != null) {
|
||||
@@ -246,8 +256,39 @@ android {
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaLanguageVersion.of(cordovaConfig.JAVA_SOURCE_COMPATIBILITY)
|
||||
targetCompatibility JavaLanguageVersion.of(cordovaConfig.JAVA_TARGET_COMPATIBILITY)
|
||||
}
|
||||
|
||||
if (cordovaConfig.IS_GRADLE_PLUGIN_KOTLIN_ENABLED) {
|
||||
if (cordovaConfig.KOTLIN_JVM_TARGET == null) {
|
||||
// If the value is null, fallback to JAVA_TARGET_COMPATIBILITY,
|
||||
// as they generally should be equal
|
||||
def javaTarget = JavaLanguageVersion.of(cordovaConfig.JAVA_TARGET_COMPATIBILITY)
|
||||
|
||||
// check if javaTarget is <= 8; if so, we need to prefix it with "1."
|
||||
// Starting with 9 and later, the value can be used as is.
|
||||
if (javaTarget.compareTo(JavaLanguageVersion.of(8)) <= 0) {
|
||||
javaTarget = "1." + javaTarget
|
||||
}
|
||||
|
||||
cordovaConfig.KOTLIN_JVM_TARGET = javaTarget
|
||||
}
|
||||
|
||||
// Similar to above, check if kotlin target is <= 8, if so prefix it.
|
||||
// This allows the user to use consistent set of values in config.xml
|
||||
// Rather than having to be aware whether the "1."" prefix is needed.
|
||||
// This check is only done if the value isn't already prefixed with 1.
|
||||
if (
|
||||
!cordovaConfig.KOTLIN_JVM_TARGET.startsWith("1.") &&
|
||||
JavaLanguageVersion.of(cordovaConfig.KOTLIN_JVM_TARGET).compareTo(JavaLanguageVersion.of(8)) <= 0
|
||||
) {
|
||||
cordovaConfig.KOTLIN_JVM_TARGET = "1." + cordovaConfig.KOTLIN_JVM_TARGET
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = cordovaConfig.KOTLIN_JVM_TARGET
|
||||
}
|
||||
}
|
||||
|
||||
if (cdvReleaseSigningPropertiesFile) {
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
# 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.
|
||||
|
||||
|
||||
# This file was originally created by the Android Tools, but is now
|
||||
# used by cordova-android to manage the state of the various third party
|
||||
# libraries used in your application
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background" />
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
<monochrome android:drawable="@mipmap/ic_launcher_monochrome" />
|
||||
</adaptive-icon>
|
||||
|
||||
BIN
templates/project/res/mipmap-hdpi-v26/ic_launcher_monochrome.png
Normal file
BIN
templates/project/res/mipmap-hdpi-v26/ic_launcher_monochrome.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -1,5 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background" />
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
<monochrome android:drawable="@mipmap/ic_launcher_monochrome" />
|
||||
</adaptive-icon>
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background" />
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
<monochrome android:drawable="@mipmap/ic_launcher_monochrome" />
|
||||
</adaptive-icon>
|
||||
|
||||
BIN
templates/project/res/mipmap-mdpi-v26/ic_launcher_monochrome.png
Normal file
BIN
templates/project/res/mipmap-mdpi-v26/ic_launcher_monochrome.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 916 B |
@@ -1,5 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background" />
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
<monochrome android:drawable="@mipmap/ic_launcher_monochrome" />
|
||||
</adaptive-icon>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
@@ -1,5 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background" />
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
<monochrome android:drawable="@mipmap/ic_launcher_monochrome" />
|
||||
</adaptive-icon>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
@@ -1,5 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background" />
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
<monochrome android:drawable="@mipmap/ic_launcher_monochrome" />
|
||||
</adaptive-icon>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
@@ -1,4 +1,5 @@
|
||||
/*
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
@@ -15,18 +16,15 @@
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
-->
|
||||
<!--
|
||||
Note: This File provider should only be used by the Cordova core
|
||||
itself and should not be used for responding to Intents because
|
||||
it will expose the app's private data folders.
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* This script is to be run manually (e.g. by npm run clean:java-unit-tests) if
|
||||
* you want to upgrade gradlew or test its proper generation.
|
||||
*/
|
||||
|
||||
for (const variant of ['androidx']) {
|
||||
for (const file of ['gradlew', 'gradlew.bat']) {
|
||||
fs.removeSync(path.join(__dirname, variant, file));
|
||||
}
|
||||
}
|
||||
For more information about FileProviders see:
|
||||
https://developer.android.com/reference/androidx/core/content/FileProvider
|
||||
-->
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<cache-path name="cache" path="." />
|
||||
</paths>
|
||||
29
templates/project/tools/settings.gradle
Normal file
29
templates/project/tools/settings.gradle
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// This is an empty project used to provide Gradle Tooling
|
||||
// Using the main project which loads AGP will enforce a minimum version
|
||||
// requirement on the end-user, requiring a gradle install that satisfies AGP
|
||||
// version requirements.
|
||||
// To avoid that, we utilise this empty project of which we can
|
||||
// freely run the gradle wrapper task against to obtain the
|
||||
// wrapper at the desired version, without being restricted by AGP's version
|
||||
// requirements.
|
||||
// Of course, the installed wrapper must still be of at least the minimum
|
||||
// required version of AGP for the build to work correctly.
|
||||
@@ -1 +0,0 @@
|
||||
//This file is intentionally just a comment
|
||||
@@ -24,6 +24,8 @@ android {
|
||||
compileSdkVersion cordovaConfig.COMPILE_SDK_VERSION
|
||||
buildToolsVersion cordovaConfig.BUILD_TOOLS_VERSION
|
||||
|
||||
namespace 'org.apache.cordova.unittests'
|
||||
|
||||
defaultConfig {
|
||||
applicationId "org.apache.cordova.unittests"
|
||||
minSdkVersion cordovaConfig.MIN_SDK_VERSION
|
||||
|
||||
@@ -69,30 +69,35 @@ public class BackButtonMultipageTest {
|
||||
assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
|
||||
|
||||
mActivityRule.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
webInterface.sendJavascript("window.location = 'sample2.html';");
|
||||
}
|
||||
});
|
||||
assertPageSample(SAMPLE2_URL);
|
||||
mActivityRule.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
webInterface.sendJavascript("window.location = 'sample3.html';");
|
||||
}
|
||||
});
|
||||
assertPageSample(SAMPLE3_URL);
|
||||
mActivityRule.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
assertTrue(webInterface.backHistory());
|
||||
}
|
||||
});
|
||||
assertPageSample(SAMPLE2_URL);
|
||||
mActivityRule.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
assertTrue(webInterface.backHistory());
|
||||
}
|
||||
});
|
||||
assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
|
||||
mActivityRule.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
assertFalse(webInterface.backHistory());
|
||||
}
|
||||
@@ -105,30 +110,35 @@ public class BackButtonMultipageTest {
|
||||
assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
|
||||
|
||||
mActivityRule.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
webInterface.loadUrl(SAMPLE2_URL);
|
||||
}
|
||||
});
|
||||
assertPageSample(SAMPLE2_URL);
|
||||
mActivityRule.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
webInterface.loadUrl(SAMPLE3_URL);
|
||||
}
|
||||
});
|
||||
assertPageSample(SAMPLE3_URL);
|
||||
mActivityRule.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
assertTrue(webInterface.backHistory());
|
||||
}
|
||||
});
|
||||
assertPageSample(SAMPLE2_URL);
|
||||
mActivityRule.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
assertTrue(webInterface.backHistory());
|
||||
}
|
||||
});
|
||||
assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
|
||||
mActivityRule.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
assertFalse(webInterface.backHistory());
|
||||
}
|
||||
@@ -141,12 +151,14 @@ public class BackButtonMultipageTest {
|
||||
assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
|
||||
|
||||
mActivityRule.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
webInterface.loadUrl(SAMPLE2_URL);
|
||||
}
|
||||
});
|
||||
assertPageSample(SAMPLE2_URL);
|
||||
mActivityRule.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
webInterface.loadUrl(SAMPLE3_URL);
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ public class ErrorUrlTest {
|
||||
assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
|
||||
assertEquals(ERROR_URL, mActivity.onPageFinishedUrl.take());
|
||||
mActivityRule.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
assertEquals(ERROR_URL, mActivity.getWebInterface().getUrl());
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ public class MessageChannelMultipageTest {
|
||||
//load a page - this resets the plugin manager and nulls cordovaWebViewImpl.appPlugin
|
||||
//(previously this resets plugin manager but did not null cordovaWebViewImpl.appPlugin, leading to the issue)
|
||||
mActivityRule.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cordovaWebView.loadUrl(START_URL);
|
||||
}
|
||||
|
||||
@@ -17,11 +17,10 @@
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.apache.cordova.unittests">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
|
||||
@@ -93,6 +93,7 @@ public class EmbeddedWebViewActivity extends AppCompatActivity {
|
||||
* @param permissions
|
||||
* @param grantResults
|
||||
*/
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String permissions[],
|
||||
int[] grantResults) {
|
||||
try
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
# 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.
|
||||
|
||||
# Project-wide Gradle settings.
|
||||
|
||||
# IDE (e.g. Android Studio) users:
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
wrapper {
|
||||
apply from: '../../framework/cordova.gradle'
|
||||
gradleVersion = cordovaConfig.GRADLE_VERSION
|
||||
}
|
||||
@@ -28,7 +28,7 @@ class AndroidTestRunner {
|
||||
constructor (testTitle, projectDir) {
|
||||
this.testTitle = testTitle;
|
||||
this.projectDir = projectDir;
|
||||
this.gradleWrapper = path.join(this.projectDir, 'gradlew');
|
||||
this.gradleWrapper = path.join(this.projectDir, 'tools/gradlew');
|
||||
}
|
||||
|
||||
_gradlew (...args) {
|
||||
@@ -42,8 +42,18 @@ class AndroidTestRunner {
|
||||
);
|
||||
}
|
||||
|
||||
_getGradleVersion () {
|
||||
const config = JSON.parse(
|
||||
fs.readFileSync(path.resolve(this.projectDir, '../../framework/cdv-gradle-config-defaults.json'), {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
);
|
||||
|
||||
return config.GRADLE_VERSION;
|
||||
}
|
||||
|
||||
_createProjectBuilder () {
|
||||
return new ProjectBuilder(this.projectDir).runGradleWrapper('gradle');
|
||||
return new ProjectBuilder(this.projectDir).installGradleWrapper(this._getGradleVersion());
|
||||
}
|
||||
|
||||
run () {
|
||||
@@ -52,15 +62,16 @@ class AndroidTestRunner {
|
||||
.then(_ => {
|
||||
// TODO we should probably not only copy these files, but instead create a new project from scratch
|
||||
fs.copyFileSync(path.resolve(this.projectDir, '../../framework/cdv-gradle-config-defaults.json'), path.resolve(this.projectDir, 'cdv-gradle-config.json'));
|
||||
fs.copySync(path.resolve(this.projectDir, '../../templates/project/tools'), path.resolve(this.projectDir, 'tools'));
|
||||
fs.copyFileSync(
|
||||
path.join(__dirname, '../templates/project/assets/www/cordova.js'),
|
||||
path.join(this.projectDir, 'app/src/main/assets/www/cordova.js')
|
||||
);
|
||||
})
|
||||
.then(_ => this._createProjectBuilder())
|
||||
.then(_ => this._gradlew('--version'))
|
||||
.then(_ => this._gradlew(['--version']))
|
||||
.then(_ => console.log(`[${this.testTitle}] Gradle wrapper is ready. Running tests now.`))
|
||||
.then(_ => this._gradlew('test'))
|
||||
.then(_ => this._gradlew(['test']))
|
||||
.then(_ => console.log(`[${this.testTitle}] Java unit tests completed successfully`));
|
||||
}
|
||||
}
|
||||
|
||||
26
types/index.d.ts
vendored
Normal file
26
types/index.d.ts
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
interface Navigator {
|
||||
/** This plugin displays and hides a splash screen during application launch. */
|
||||
splashscreen: {
|
||||
/** Dismiss the splash screen. */
|
||||
hide(): void;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user