mirror of
https://github.com/apache/cordova-android.git
synced 2026-01-30 00:05:28 +08:00
Compare commits
95 Commits
12.0.0
...
GitToTheHu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10437f82ae | ||
|
|
81b22c3a87 | ||
|
|
d7afba0ad0 | ||
|
|
ebe6890dee | ||
|
|
172b8448af | ||
|
|
655aa0a5fb | ||
|
|
52578ae770 | ||
|
|
360be21ea5 | ||
|
|
5bca218e5b | ||
|
|
eb5fe4fbda | ||
|
|
46af3114b8 | ||
|
|
76aa938002 | ||
|
|
7d7f511023 | ||
|
|
488c498733 | ||
|
|
e4457f7fdb | ||
|
|
8742cfe4a6 | ||
|
|
af1ae68a97 | ||
|
|
56afb70894 | ||
|
|
1204a79378 | ||
|
|
bf0ba3dd5e | ||
|
|
c2cf589d84 | ||
|
|
0190d2e33a | ||
|
|
df36c7a2c4 | ||
|
|
b79232612b | ||
|
|
60d2842024 | ||
|
|
cab5c5b7ec | ||
|
|
484c60e483 | ||
|
|
72e7148884 | ||
|
|
36bee66493 | ||
|
|
4dcfc361d2 | ||
|
|
c25ed27828 | ||
|
|
d8f6f37737 | ||
|
|
f0e8885693 | ||
|
|
5dc9c72821 | ||
|
|
7a47fe01dc | ||
|
|
08b8a95416 | ||
|
|
6024465814 | ||
|
|
6b8e819f21 | ||
|
|
00744c4f71 | ||
|
|
872d98876e | ||
|
|
855fab238c | ||
|
|
2ffe68ab17 | ||
|
|
f697ca7dec | ||
|
|
1d82a3b52f | ||
|
|
688d2cf5ad | ||
|
|
f6e384a9ea | ||
|
|
839f9b878b | ||
|
|
d4eca414e3 | ||
|
|
5da9bd6d9d | ||
|
|
2258d33a72 | ||
|
|
ca4caf3fc1 | ||
|
|
7ab18487cf | ||
|
|
ff11f659f0 | ||
|
|
aad36fe565 | ||
|
|
7f9529408b | ||
|
|
bb4f86e7b9 | ||
|
|
d0b59863ac | ||
|
|
7544fdf1ed | ||
|
|
8f458b042b | ||
|
|
eb0f002112 | ||
|
|
e012478537 | ||
|
|
b623311efa | ||
|
|
1f349f2984 | ||
|
|
9f5518000f | ||
|
|
92116dee48 | ||
|
|
34220ae0e3 | ||
|
|
1fe44d71c5 | ||
|
|
58c2e3ae15 | ||
|
|
ea045dee63 | ||
|
|
cee7b0b8ac | ||
|
|
6f0efd3a0d | ||
|
|
dff2fc6331 | ||
|
|
1347e48d14 | ||
|
|
5a2c50d1ed | ||
|
|
5eddc460e4 | ||
|
|
3503bfa31b | ||
|
|
d281727113 | ||
|
|
172e947d18 | ||
|
|
2143045d4e | ||
|
|
3c5df42df5 | ||
|
|
c2f315c0ff | ||
|
|
89a0a72da5 | ||
|
|
ed8e5d2f0a | ||
|
|
7fa4a65d0a | ||
|
|
b773ae48f4 | ||
|
|
ebf0b105a3 | ||
|
|
9261b29cf2 | ||
|
|
90e74befc7 | ||
|
|
97806db463 | ||
|
|
4742358601 | ||
|
|
e61e271f5b | ||
|
|
0c805a0a8e | ||
|
|
a7cd4227a4 | ||
|
|
c9e7c59986 | ||
|
|
94234d988e |
@@ -1,3 +0,0 @@
|
||||
templates/project/assets/www/cordova.js
|
||||
test/android/app
|
||||
test/androidx/app
|
||||
20
.gitattributes
vendored
20
.gitattributes
vendored
@@ -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.
|
||||
|
||||
* text eol=lf
|
||||
|
||||
# source code
|
||||
@@ -23,7 +40,7 @@
|
||||
*.scm text
|
||||
*.sql text
|
||||
*.sh text
|
||||
*.bat text
|
||||
*.bat text eol=crlf
|
||||
|
||||
# templates
|
||||
*.ejs text
|
||||
@@ -92,3 +109,4 @@ AUTHORS text
|
||||
*.woff binary
|
||||
*.pyc binary
|
||||
*.pdf binary
|
||||
*.jar binary
|
||||
|
||||
54
.github/workflows/ci.yml
vendored
54
.github/workflows/ci.yml
vendored
@@ -6,18 +6,28 @@
|
||||
# "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
|
||||
# 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
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
name: Node CI
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'dependabot/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
jobs:
|
||||
test:
|
||||
@@ -27,22 +37,19 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x, 18.x, 20.x]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
node-version: [20.x, 22.x, 24.x]
|
||||
os: [ubuntu-latest, windows-latest, macos-15]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: set up JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
|
||||
- name: Environment Information
|
||||
run: |
|
||||
@@ -50,6 +57,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,8 +79,12 @@ jobs:
|
||||
env:
|
||||
CI: true
|
||||
|
||||
- name: upload coverage
|
||||
- uses: github/codeql-action/analyze@v3
|
||||
|
||||
# v4.6.0
|
||||
- uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238
|
||||
if: success()
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
name: ${{ runner.os }} node.js ${{ matrix.node-version }}
|
||||
token: ${{ secrets.CORDOVA_CODECOV_TOKEN }}
|
||||
fail_ci_if_error: false
|
||||
|
||||
55
.github/workflows/release-audit.yml
vendored
Normal file
55
.github/workflows/release-audit.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
# 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:
|
||||
branches-ignore:
|
||||
- 'dependabot/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Audit Licenses
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Checkout project
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
# Check license headers (v2.0.0)
|
||||
- uses: erisu/apache-rat-action@46fb01ce7d8f76bdcd7ab10e7af46e1ea95ca01c
|
||||
|
||||
# Setup environment with node
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
# Install node packages
|
||||
- name: npm install packages
|
||||
run: npm ci
|
||||
|
||||
# Check node package licenses (v2.0.1)
|
||||
- uses: erisu/license-checker-action@99cffa11264fe545fd0baa6c13bca5a00ae608f2
|
||||
with:
|
||||
license-config: 'licence_checker.yml'
|
||||
include-asf-category-a: true
|
||||
27
.gitignore
vendored
27
.gitignore
vendored
@@ -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.
|
||||
|
||||
.DS_Store
|
||||
.gradle
|
||||
.metadata
|
||||
@@ -29,12 +46,11 @@ 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/repositories.gradle
|
||||
/test/androidx/app/repositories.gradle
|
||||
/test/androidx/tools
|
||||
/test/androidx/build
|
||||
/test/assets/www/.tmp*
|
||||
/test/assets/www/cordova.js
|
||||
/test/bin
|
||||
@@ -50,7 +66,6 @@ tmp/**/*
|
||||
npm-debug.log
|
||||
node_modules/
|
||||
coverage/
|
||||
.nyc_output/
|
||||
# Eclipse Buildship files
|
||||
.project
|
||||
.settings
|
||||
|
||||
17
.npmignore
17
.npmignore
@@ -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.
|
||||
|
||||
.*
|
||||
coverage
|
||||
test
|
||||
|
||||
18
.npmrc
18
.npmrc
@@ -1,2 +1,18 @@
|
||||
registry=https://registry.npmjs.org
|
||||
# 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.
|
||||
|
||||
registry=https://registry.npmjs.org
|
||||
|
||||
35
.ratignore
35
.ratignore
@@ -1,8 +1,27 @@
|
||||
*.properties
|
||||
templates
|
||||
gen
|
||||
proguard-project.txt
|
||||
spec
|
||||
framework/build
|
||||
ic_launcher.png
|
||||
build
|
||||
# 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.
|
||||
|
||||
.git/
|
||||
coverage/
|
||||
framework/build/
|
||||
node_modules/
|
||||
spec/fixtures/
|
||||
templates/project/gitignore
|
||||
test/androidx/app/.gitignore
|
||||
test/androidx/app/build/
|
||||
test/androidx/build/
|
||||
test/androidx/tools/
|
||||
|
||||
@@ -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.
|
||||
|
||||
#
|
||||
# Settings for post-review (used for uploading diffs to reviews.apache.org).
|
||||
#
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
|
||||
# Cordova Android
|
||||
|
||||
[](https://nodei.co/npm/cordova-android/)
|
||||
|
||||
[)](https://npmjs.com/package/cordova-android)
|
||||
[)](https://github.com/apache/cordova-android)
|
||||
[](https://github.com/apache/cordova-android/actions?query=branch%3Amaster)
|
||||
[](https://codecov.io/github/apache/cordova-android?branch=master)
|
||||
|
||||
@@ -32,7 +32,7 @@ Cordova Android is an Android application library that allows for Cordova-based
|
||||
|
||||
## Requirements
|
||||
|
||||
* Java Development Kit (JDK) 11
|
||||
* Java Development Kit (JDK) 17
|
||||
* [Android SDK](https://developer.android.com/)
|
||||
* [Node.js](https://nodejs.org)
|
||||
|
||||
|
||||
@@ -20,6 +20,98 @@
|
||||
-->
|
||||
## Release Notes for Cordova (Android)
|
||||
|
||||
### 14.0.1 (Apr 24, 2025)
|
||||
|
||||
**Fixes:**
|
||||
|
||||
* [GH-1795](https://github.com/apache/cordova-android/pull/1795) fix: configure gradle `java.home`
|
||||
* [GH-1793](https://github.com/apache/cordova-android/pull/1793) fix(windows): get gradle path with `which` command
|
||||
|
||||
### 14.0.0 (Mar 23, 2025)
|
||||
|
||||
**Breaking Changes:**
|
||||
|
||||
* [GH-1788](https://github.com/apache/cordova-android/pull/1788) dep!: bump npm packages
|
||||
* nyc@17.1.0
|
||||
* which@5.0.0
|
||||
* semver@7.7.1
|
||||
* jasmine@5.6.0
|
||||
* android-versions@2.1.0
|
||||
* cordova-common@5.0.1
|
||||
* fast-glob@3.3.3
|
||||
* nopt@8.1.0
|
||||
* [GH-1789](https://github.com/apache/cordova-android/pull/1789) feat!: bump node engine requirement `>=20.5.0`
|
||||
* [GH-1784](https://github.com/apache/cordova-android/pull/1784) feat!: bump java default targets to 11
|
||||
* [GH-1771](https://github.com/apache/cordova-android/pull/1771) feat!: deprecate CordovaPlugin's method initialize
|
||||
* [GH-1767](https://github.com/apache/cordova-android/pull/1767) feat!: use kotlin-stdlib instead of kotlin-stdlib-jdk*
|
||||
* [GH-1763](https://github.com/apache/cordova-android/pull/1763) feat!: SDK 35 Support
|
||||
|
||||
**Features:**
|
||||
|
||||
* [GH-1785](https://github.com/apache/cordova-android/pull/1785) feat: bump gradle to 8.13
|
||||
* [GH-1779](https://github.com/apache/cordova-android/pull/1779) feat: add `AndroidEdgeToEdge` preference & theme flag
|
||||
* [GH-1778](https://github.com/apache/cordova-android/pull/1778) feat: Account for Node security patch
|
||||
* [GH-1768](https://github.com/apache/cordova-android/pull/1768) feat: `androidx.core:core-splashscreen@1.0.1`
|
||||
* [GH-1766](https://github.com/apache/cordova-android/pull/1766) feat: `com.google.gms:google-services@4.4.2`
|
||||
* [GH-1765](https://github.com/apache/cordova-android/pull/1765) feat: `androidx.webkit:webkit@1.12.1`
|
||||
* [GH-1764](https://github.com/apache/cordova-android/pull/1764) feat: `androidx.appcompat:appcompat@1.7.0`
|
||||
|
||||
**Fixes:**
|
||||
|
||||
* [GH-1790](https://github.com/apache/cordova-android/pull/1790) fix: replace fs-extra.ensureFileSync with fs.writeFileSync
|
||||
* [GH-1781](https://github.com/apache/cordova-android/pull/1781) fix: copy gradle wrapper from tools to platform root dir
|
||||
* [GH-1770](https://github.com/apache/cordova-android/pull/1770) fix: creation of cdv-gradle-config.json w/ --link flag
|
||||
* [GH-1739](https://github.com/apache/cordova-android/pull/1739) fix(docs): Incorrect JDK requirement stated in README
|
||||
* [GH-1718](https://github.com/apache/cordova-android/pull/1718) fix: app restart when BT keyboard is connected in some devices
|
||||
|
||||
**Chores & Refactoring:**
|
||||
|
||||
* [GH-1786](https://github.com/apache/cordova-android/pull/1786) chore: add AndroidX build test to gitignore
|
||||
* [GH-1774](https://github.com/apache/cordova-android/pull/1774) style: update & resolve doc block warnings
|
||||
* [GH-1772](https://github.com/apache/cordova-android/pull/1772) refactor: replace fs-extra with node:fs
|
||||
* [GH-1769](https://github.com/apache/cordova-android/pull/1769) refactor: prefix node:*
|
||||
* [GH-1748](https://github.com/apache/cordova-android/pull/1748) chore(deps): bump cross-spawn from 7.0.3 to 7.0.6
|
||||
* [GH-1750](https://github.com/apache/cordova-android/pull/1750) chore(ci): Fix dependabot PR failures
|
||||
* [GH-1736](https://github.com/apache/cordova-android/pull/1736) chore(deps): bump micromatch from 4.0.5 to 4.0.8
|
||||
* [GH-1716](https://github.com/apache/cordova-android/pull/1716) chore(deps): bump braces from 3.0.2 to 3.0.3
|
||||
|
||||
### 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:**
|
||||
|
||||
4
cordova-js-src/exec.js
vendored
4
cordova-js-src/exec.js
vendored
@@ -91,7 +91,7 @@ function androidExec (success, fail, service, action, args) {
|
||||
var callbackId = service + cordova.callbackId++;
|
||||
var argsJson = JSON.stringify(args);
|
||||
if (success || fail) {
|
||||
cordova.callbacks[callbackId] = { success: success, fail: fail };
|
||||
cordova.callbacks[callbackId] = { success, fail };
|
||||
}
|
||||
|
||||
var msgs = nativeApiProvider.get().exec(bridgeSecret, service, action, callbackId, argsJson);
|
||||
@@ -131,6 +131,8 @@ function pollOnce (opt_fromOnlineEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
androidExec.pollOnce = pollOnce;
|
||||
|
||||
function pollingTimerFunc () {
|
||||
if (pollEnabled) {
|
||||
pollOnce();
|
||||
|
||||
4
cordova-js-src/platform.js
vendored
4
cordova-js-src/platform.js
vendored
@@ -39,6 +39,10 @@ module.exports = {
|
||||
// Core Splash Screen
|
||||
modulemapper.clobbers('cordova/plugin/android/splashscreen', 'navigator.splashscreen');
|
||||
|
||||
// Attach the internal statusBar utility to window.statusbar
|
||||
// see the file under plugin/android/statusbar.js
|
||||
modulemapper.clobbers('cordova/plugin/android/statusbar', 'window.statusbar');
|
||||
|
||||
var APP_PLUGIN_NAME = Number(cordova.platformVersion.split('.')[0]) >= 4 ? 'CoreAndroid' : 'App';
|
||||
|
||||
// Inject a listener for the backbutton on the document.
|
||||
|
||||
4
cordova-js-src/plugin/android/app.js
vendored
4
cordova-js-src/plugin/android/app.js
vendored
@@ -31,14 +31,14 @@ module.exports = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the url into the webview or into new browser instance.
|
||||
* Load the url into the WebView or into new browser instance.
|
||||
*
|
||||
* @param url The URL to load
|
||||
* @param props Properties that can be passed in to the activity:
|
||||
* wait: int => wait msec before loading URL
|
||||
* loadingDialog: "Title,Message" => display a native loading dialog
|
||||
* loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error
|
||||
* clearHistory: boolean => clear webview history (default=false)
|
||||
* clearHistory: boolean => clear WebView history (default=false)
|
||||
* openExternal: boolean => open in a new browser (default=false)
|
||||
*
|
||||
* Example:
|
||||
|
||||
93
cordova-js-src/plugin/android/statusbar.js
vendored
Normal file
93
cordova-js-src/plugin/android/statusbar.js
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
var exec = require('cordova/exec');
|
||||
|
||||
var statusBarVisible = true;
|
||||
var statusBar = {};
|
||||
|
||||
// This <script> element is explicitly used by Cordova's statusbar for computing color. (Do not use this element)
|
||||
const statusBarScript = document.createElement('script');
|
||||
document.head.appendChild(statusBarScript);
|
||||
|
||||
Object.defineProperty(statusBar, 'visible', {
|
||||
configurable: false,
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
if (window.StatusBar) {
|
||||
// try to let the StatusBar plugin handle it
|
||||
return window.StatusBar.isVisible;
|
||||
}
|
||||
|
||||
return statusBarVisible;
|
||||
},
|
||||
set: function (value) {
|
||||
if (window.StatusBar) {
|
||||
// try to let the StatusBar plugin handle it
|
||||
if (value) {
|
||||
window.StatusBar.show();
|
||||
} else {
|
||||
window.StatusBar.hide();
|
||||
}
|
||||
} else {
|
||||
statusBarVisible = value;
|
||||
exec(null, null, 'SystemBarPlugin', 'setStatusBarVisible', [!!value]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(statusBar, 'setBackgroundColor', {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
value: function (value) {
|
||||
statusBarScript.style.color = value;
|
||||
var rgbStr = window.getComputedStyle(statusBarScript).getPropertyValue('color');
|
||||
|
||||
if (!rgbStr.match(/^rgb/)) { return; }
|
||||
|
||||
var rgbVals = rgbStr.match(/\d+/g).map(function (v) { return parseInt(v, 10); });
|
||||
|
||||
if (rgbVals.length < 3) {
|
||||
return;
|
||||
} else if (rgbVals.length === 3) {
|
||||
rgbVals = [255].concat(rgbVals);
|
||||
}
|
||||
|
||||
// TODO: Use `padStart(2, '0')` once SDK 24 is dropped.
|
||||
const padRgb = (val) => {
|
||||
const hex = val.toString(16);
|
||||
return hex.length === 1 ? '0' + hex : hex;
|
||||
};
|
||||
const a = padRgb(rgbVals[0]);
|
||||
const r = padRgb(rgbVals[1]);
|
||||
const g = padRgb(rgbVals[2]);
|
||||
const b = padRgb(rgbVals[3]);
|
||||
const hexStr = '#' + a + r + g + b;
|
||||
|
||||
if (window.StatusBar) {
|
||||
window.StatusBar.backgroundColorByHexString(hexStr);
|
||||
} else {
|
||||
exec(null, null, 'SystemBarPlugin', 'setStatusBarBackgroundColor', rgbVals);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = statusBar;
|
||||
66
eslint.config.js
Normal file
66
eslint.config.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
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 { defineConfig, globalIgnores } = require('eslint/config');
|
||||
const nodeConfig = require('@cordova/eslint-config/node');
|
||||
const nodeTestConfig = require('@cordova/eslint-config/node-tests');
|
||||
const browserConfig = require('@cordova/eslint-config/browser');
|
||||
|
||||
module.exports = defineConfig([
|
||||
globalIgnores([
|
||||
'**/coverage/',
|
||||
'spec/fixtures/',
|
||||
'templates/project/assets/www/cordova.js',
|
||||
'test/android/app',
|
||||
'test/androidx/app'
|
||||
]),
|
||||
{
|
||||
// Include these JavaScript files that do not have file extensions.
|
||||
files: [
|
||||
'templates/cordova/version',
|
||||
'templates/cordova/android_sdk_version',
|
||||
'templates/cordova/lib/list-devices',
|
||||
'templates/cordova/lib/list-emulator-images'
|
||||
]
|
||||
},
|
||||
...nodeConfig,
|
||||
...nodeTestConfig.map(config => ({
|
||||
files: ['spec/**/*.js'],
|
||||
...config,
|
||||
rules: {
|
||||
...(config.rules || {}),
|
||||
'prefer-promise-reject-errors': 'off'
|
||||
}
|
||||
})),
|
||||
...browserConfig.map(config => ({
|
||||
files: [
|
||||
'cordova-js-src/**/*.js',
|
||||
'templates/project/assets/**/*.js'
|
||||
],
|
||||
...config,
|
||||
languageOptions: {
|
||||
...(config?.languageOptions || {}),
|
||||
globals: {
|
||||
...(config.languageOptions?.globals || {}),
|
||||
require: 'readonly',
|
||||
module: 'readonly'
|
||||
}
|
||||
}
|
||||
}))
|
||||
]);
|
||||
@@ -44,14 +44,14 @@ allprojects {
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
namespace 'org.apache.cordova'
|
||||
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
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
{
|
||||
"MIN_SDK_VERSION": 24,
|
||||
"SDK_VERSION": 33,
|
||||
"SDK_VERSION": 36,
|
||||
"COMPILE_SDK_VERSION": null,
|
||||
"GRADLE_VERSION": "7.6",
|
||||
"MIN_BUILD_TOOLS_VERSION": "33.0.2",
|
||||
"AGP_VERSION": "7.4.2",
|
||||
"KOTLIN_VERSION": "1.7.21",
|
||||
"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",
|
||||
"GRADLE_VERSION": "8.14.2",
|
||||
"MIN_BUILD_TOOLS_VERSION": "36.0.0",
|
||||
"AGP_VERSION": "8.10.1",
|
||||
"KOTLIN_VERSION": "2.1.21",
|
||||
"ANDROIDX_APP_COMPAT_VERSION": "1.7.1",
|
||||
"ANDROIDX_WEBKIT_VERSION": "1.14.0",
|
||||
"ANDROIDX_CORE_SPLASHSCREEN_VERSION": "1.0.1",
|
||||
"GRADLE_PLUGIN_GOOGLE_SERVICES_VERSION": "4.4.2",
|
||||
"IS_GRADLE_PLUGIN_GOOGLE_SERVICES_ENABLED": false,
|
||||
"IS_GRADLE_PLUGIN_KOTLIN_ENABLED": false,
|
||||
"PACKAGE_NAMESPACE": "io.cordova.helloCordova"
|
||||
"PACKAGE_NAMESPACE": "org.apache.cordova.hellocordova",
|
||||
"JAVA_SOURCE_COMPATIBILITY": 11,
|
||||
"JAVA_TARGET_COMPATIBILITY": 11,
|
||||
"KOTLIN_JVM_TARGET": null
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -146,7 +150,7 @@ def doGetConfigPreference(name, defaultValue) {
|
||||
}
|
||||
|
||||
def doApplyCordovaConfigCustomization() {
|
||||
// Apply user overide properties that comes from the "--gradleArg=-P" parameters
|
||||
// Apply user override properties that comes from the "--gradleArg=-P" parameters
|
||||
if (project.hasProperty('cdvMinSdkVersion')) {
|
||||
cordovaConfig.MIN_SDK_VERSION = Integer.parseInt('' + cdvMinSdkVersion)
|
||||
}
|
||||
@@ -236,10 +240,8 @@ ext {
|
||||
}
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
apply from: 'repositories.gradle'
|
||||
repositories repos
|
||||
|
||||
dependencies {
|
||||
classpath 'io.github.g00fy2:versioncompare:1.4.1@jar'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -39,7 +39,7 @@ public class AllowListPlugin extends CordovaPlugin {
|
||||
// Used when instantiated via reflection by PluginManager
|
||||
public AllowListPlugin() { }
|
||||
|
||||
// These can be used by embedders to allow Java-configuration of an allow list.
|
||||
// These can be used by plugin developers to allow Java-configuration of an allow list.
|
||||
public AllowListPlugin(Context context) {
|
||||
this(new AllowList(), new AllowList(), null);
|
||||
new CustomConfigXmlParser().parse(context);
|
||||
|
||||
@@ -37,8 +37,7 @@ public class AuthenticationToken {
|
||||
/**
|
||||
* Sets the user name.
|
||||
*
|
||||
* @param userName
|
||||
* the new user name
|
||||
* @param userName the new user name
|
||||
*/
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
@@ -56,8 +55,7 @@ public class AuthenticationToken {
|
||||
/**
|
||||
* Sets the password.
|
||||
*
|
||||
* @param password
|
||||
* the new password
|
||||
* @param password the new password
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
|
||||
@@ -54,7 +54,7 @@ public class CallbackMap {
|
||||
* obtained from registerCallback()
|
||||
*
|
||||
* @param mappedId The request code obtained from registerCallback()
|
||||
* @return The CordovaPlugin and orignal request code that correspond to the
|
||||
* @return The CordovaPlugin and original request code that correspond to the
|
||||
* given mappedCode
|
||||
*/
|
||||
public synchronized Pair<CordovaPlugin, Integer> getAndRemoveCallback(int mappedId) {
|
||||
|
||||
@@ -23,7 +23,7 @@ import java.util.List;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
@Deprecated // Use AllowList, CordovaPrefences, etc. directly.
|
||||
@Deprecated // Use AllowList, CordovaPreferences, etc. directly.
|
||||
public class Config {
|
||||
private static final String TAG = "Config";
|
||||
|
||||
|
||||
@@ -77,6 +77,14 @@ public class ConfigXmlParser {
|
||||
)
|
||||
);
|
||||
|
||||
pluginEntries.add(
|
||||
new PluginEntry(
|
||||
SystemBarPlugin.PLUGIN_NAME,
|
||||
"org.apache.cordova.SystemBarPlugin",
|
||||
true
|
||||
)
|
||||
);
|
||||
|
||||
pluginEntries.add(
|
||||
new PluginEntry(
|
||||
SplashScreenPlugin.PLUGIN_NAME,
|
||||
|
||||
@@ -29,9 +29,10 @@ import android.annotation.SuppressLint;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Color;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
@@ -42,14 +43,18 @@ import android.webkit.WebViewClient;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.splashscreen.SplashScreen;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
/**
|
||||
* This class is the main Android activity that represents the Cordova
|
||||
* application. It should be extended by the user to load the specific
|
||||
* html file that contains the application.
|
||||
*
|
||||
* As an example:
|
||||
* <p>As an example:</p>
|
||||
*
|
||||
* <pre>
|
||||
* package org.apache.cordova.examples;
|
||||
@@ -68,17 +73,16 @@ import androidx.core.splashscreen.SplashScreen;
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Cordova xml configuration: Cordova uses a configuration file at
|
||||
* res/xml/config.xml to specify its settings. See "The config.xml File"
|
||||
* guide in cordova-docs at http://cordova.apache.org/docs for the documentation
|
||||
* for the configuration. The use of the set*Property() methods is
|
||||
* deprecated in favor of the config.xml file.
|
||||
* <p>Cordova xml configuration: Cordova uses a configuration file at
|
||||
* res/xml/config.xml to specify its settings. See the "Config.xml API" documentation for
|
||||
* configuration details at <a href="https://cordova.apache.org/docs">Apache Cordova Docs</a>.</p>
|
||||
*
|
||||
* <p>The use of the set*Property() methods is deprecated in favor of the config.xml file.</p>
|
||||
*/
|
||||
public class CordovaActivity extends AppCompatActivity {
|
||||
public static String TAG = "CordovaActivity";
|
||||
|
||||
// The webview for our app
|
||||
// The WebView for our app
|
||||
protected CordovaWebView appView;
|
||||
|
||||
private static int ACTIVITY_STARTING = 0;
|
||||
@@ -101,17 +105,25 @@ public class CordovaActivity extends AppCompatActivity {
|
||||
|
||||
private SplashScreen splashScreen;
|
||||
|
||||
private boolean canEdgeToEdge = false;
|
||||
private boolean isFullScreen = false;
|
||||
|
||||
/**
|
||||
* Called when the activity is first created.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
// Handle the splash screen transition.
|
||||
splashScreen = SplashScreen.installSplashScreen(this);
|
||||
if (showInitialSplashScreen()) {
|
||||
splashScreen = SplashScreen.installSplashScreen(this);
|
||||
}
|
||||
|
||||
// need to activate preferences before super.onCreate to avoid "requestFeature() must be called before adding content" exception
|
||||
loadConfig();
|
||||
|
||||
canEdgeToEdge = preferences.getBoolean("AndroidEdgeToEdge", false)
|
||||
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM;
|
||||
|
||||
String logLevel = preferences.getString("loglevel", "ERROR");
|
||||
LOG.setLogLevel(logLevel);
|
||||
|
||||
@@ -126,7 +138,10 @@ public class CordovaActivity extends AppCompatActivity {
|
||||
LOG.d(TAG, "The SetFullscreen configuration is deprecated in favor of Fullscreen, and will be removed in a future version.");
|
||||
preferences.set("Fullscreen", true);
|
||||
}
|
||||
if (preferences.getBoolean("Fullscreen", false)) {
|
||||
|
||||
isFullScreen = preferences.getBoolean("Fullscreen", false);
|
||||
|
||||
if (isFullScreen) {
|
||||
// NOTE: use the FullscreenNotImmersive configuration key to set the activity in a REAL full screen
|
||||
// (as was the case in previous cordova versions)
|
||||
if (!preferences.getBoolean("FullscreenNotImmersive", false)) {
|
||||
@@ -158,7 +173,9 @@ public class CordovaActivity extends AppCompatActivity {
|
||||
cordovaInterface.onCordovaInit(appView.getPluginManager());
|
||||
|
||||
// Setup the splash screen based on preference settings
|
||||
cordovaInterface.pluginManager.postMessage("setupSplashScreen", splashScreen);
|
||||
if (showInitialSplashScreen()) {
|
||||
cordovaInterface.pluginManager.postMessage("setupSplashScreen", splashScreen);
|
||||
}
|
||||
|
||||
// Wire the hardware volume controls to control media if desired.
|
||||
String volumePref = preferences.getString("DefaultVolumeStream", "");
|
||||
@@ -181,32 +198,64 @@ public class CordovaActivity extends AppCompatActivity {
|
||||
//Suppressing warnings in AndroidStudio
|
||||
@SuppressWarnings({"deprecation", "ResourceType"})
|
||||
protected void createViews() {
|
||||
//Why are we setting a constant as the ID? This should be investigated
|
||||
appView.getView().setId(100);
|
||||
appView.getView().setLayoutParams(new FrameLayout.LayoutParams(
|
||||
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
|
||||
|
||||
// Root FrameLayout
|
||||
FrameLayout rootLayout = new FrameLayout(this);
|
||||
rootLayout.setLayoutParams(new FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
));
|
||||
|
||||
setContentView(appView.getView());
|
||||
// WebView
|
||||
View webView = appView.getView();
|
||||
webView.setLayoutParams(new FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
));
|
||||
|
||||
if (preferences.contains("BackgroundColor")) {
|
||||
try {
|
||||
int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK);
|
||||
// Background of activity:
|
||||
appView.getView().setBackgroundColor(backgroundColor);
|
||||
}
|
||||
catch (NumberFormatException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
// Create StatusBar view that will overlay the top inset
|
||||
View statusBarView = new View(this);
|
||||
statusBarView.setTag("statusBarView");
|
||||
|
||||
appView.getView().requestFocusFromTouch();
|
||||
// Handle Window Insets
|
||||
ViewCompat.setOnApplyWindowInsetsListener(rootLayout, (v, insets) -> {
|
||||
Insets bars = insets.getInsets(
|
||||
WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout()
|
||||
);
|
||||
|
||||
boolean isStatusBarVisible = statusBarView.getVisibility() != View.GONE;
|
||||
int top = isStatusBarVisible && !canEdgeToEdge && !isFullScreen ? bars.top : 0;
|
||||
int bottom = !canEdgeToEdge && !isFullScreen ? bars.bottom : 0;
|
||||
int left = !canEdgeToEdge && !isFullScreen ? bars.left : 0;
|
||||
int right = !canEdgeToEdge && !isFullScreen ? bars.right : 0;
|
||||
|
||||
FrameLayout.LayoutParams webViewParams = (FrameLayout.LayoutParams) webView.getLayoutParams();
|
||||
webViewParams.setMargins(left, top, right, bottom);
|
||||
webView.setLayoutParams(webViewParams);
|
||||
|
||||
FrameLayout.LayoutParams statusBarParams = new FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
top,
|
||||
Gravity.TOP
|
||||
);
|
||||
statusBarView.setLayoutParams(statusBarParams);
|
||||
|
||||
return insets;
|
||||
});
|
||||
|
||||
rootLayout.addView(webView);
|
||||
rootLayout.addView(statusBarView);
|
||||
|
||||
setContentView(rootLayout);
|
||||
rootLayout.post(() -> ViewCompat.requestApplyInsets(rootLayout));
|
||||
webView.requestFocusFromTouch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the default web view object.
|
||||
* <p/>
|
||||
* Override this to customize the webview that is used.
|
||||
* Override this to customize the WebView that is used.
|
||||
*/
|
||||
protected CordovaWebView makeWebView() {
|
||||
return new CordovaWebViewImpl(makeWebViewEngine());
|
||||
@@ -227,7 +276,7 @@ public class CordovaActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the url into the webview.
|
||||
* Load the url into the WebView.
|
||||
*/
|
||||
public void loadUrl(String url) {
|
||||
if (appView == null) {
|
||||
@@ -250,7 +299,7 @@ public class CordovaActivity extends AppCompatActivity {
|
||||
|
||||
if (this.appView != null) {
|
||||
// CB-9382 If there is an activity that started for result and main activity is waiting for callback
|
||||
// result, we shoudn't stop WebView Javascript timers, as activity for result might be using them
|
||||
// result, we shouldn't stop WebView Javascript timers, as activity for result might be using them
|
||||
boolean keepRunning = this.keepRunning || this.cordovaInterface.activityResultCallback != null;
|
||||
this.appView.handlePause(keepRunning);
|
||||
}
|
||||
@@ -391,6 +440,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 +450,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 +467,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 +476,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 +541,7 @@ public class CordovaActivity extends AppCompatActivity {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
cordovaInterface.onSaveInstanceState(outState);
|
||||
super.onSaveInstanceState(outState);
|
||||
@@ -533,4 +587,20 @@ public class CordovaActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether to show the splash screen while the WebView is initially loading.
|
||||
* <p>
|
||||
* This method is available for native apps that embed a Cordova WebView.
|
||||
* Native apps most likely already have their own splash screen setup.
|
||||
* This option is not configurable for Cordova CLI–created apps.
|
||||
*
|
||||
* @return {@code true}
|
||||
* <p>
|
||||
* To disable the initial splash screen, override this method and return {@code false}
|
||||
* in your activity that extends {@link CordovaActivity}.
|
||||
*/
|
||||
protected boolean showInitialSplashScreen() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,63 +41,70 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
|
||||
* Cancel this request
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public void cancel()
|
||||
{
|
||||
request.cancel();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the host name of the server requesting the certificate.
|
||||
/**
|
||||
* @return the host name of the server requesting the certificate.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public String getHost()
|
||||
{
|
||||
return request.getHost();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the acceptable types of asymmetric keys (can be null).
|
||||
/**
|
||||
* @return the acceptable types of asymmetric keys (can be null).
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public String[] getKeyTypes()
|
||||
{
|
||||
return request.getKeyTypes();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the port number of the server requesting the certificate.
|
||||
/**
|
||||
* @return the port number of the server requesting the certificate.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public int getPort()
|
||||
{
|
||||
return request.getPort();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the acceptable certificate issuers for the certificate matching the private key (can be null).
|
||||
/**
|
||||
* @return the acceptable certificate issuers for the certificate matching the private key (can be null).
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public Principal[] getPrincipals()
|
||||
{
|
||||
return request.getPrincipals();
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Ignore the request for now. Do not remember user's choice.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public void ignore()
|
||||
{
|
||||
request.ignore();
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Proceed with the specified private key and client certificate chain. Remember the user's positive choice and use it for future requests.
|
||||
*
|
||||
* @param privateKey The privateKey
|
||||
* @param chain The certificate chain
|
||||
*/
|
||||
@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)
|
||||
{
|
||||
@@ -111,8 +118,8 @@ public class CordovaDialogsHelper {
|
||||
* If the client returns true, WebView will assume that the client will
|
||||
* handle the prompt dialog and call the appropriate JsPromptResult method.
|
||||
*
|
||||
* Since we are hacking prompts for our own purposes, we should not be using them for
|
||||
* this purpose, perhaps we should hack console.log to do this instead!
|
||||
* <p>Since we are hacking prompts for our own purposes, we should not be using them for
|
||||
* this purpose, perhaps we should hack console.log to do this instead!</p>
|
||||
*/
|
||||
public void showPrompt(String message, String defaultValue, final Result result) {
|
||||
// Returning false would also show a dialog, but the default one shows the origin (ugly).
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@ public interface CordovaInterface {
|
||||
/**
|
||||
* Get the Android activity.
|
||||
*
|
||||
* If a custom engine lives outside of the Activity's lifecycle the return value may be null.
|
||||
* <p>If a custom engine lives outside of the Activity's lifecycle the return value
|
||||
* may be null.</p>
|
||||
*
|
||||
* @return the Activity
|
||||
*/
|
||||
@@ -74,7 +75,7 @@ public interface CordovaInterface {
|
||||
public Object onMessage(String id, Object data);
|
||||
|
||||
/**
|
||||
* Returns a shared thread pool that can be used for background tasks.
|
||||
* @return a shared thread pool that can be used for background tasks.
|
||||
*/
|
||||
public ExecutorService getThreadPool();
|
||||
|
||||
@@ -89,7 +90,9 @@ public interface CordovaInterface {
|
||||
public void requestPermissions(CordovaPlugin plugin, int requestCode, String [] permissions);
|
||||
|
||||
/**
|
||||
* Check for a permission. Returns true if the permission is granted, false otherwise.
|
||||
* Check for a permission.
|
||||
*
|
||||
* @return true if the permission is granted, false otherwise.
|
||||
*/
|
||||
public boolean hasPermission(String permission);
|
||||
|
||||
|
||||
@@ -135,7 +135,9 @@ public class CordovaInterfaceImpl implements CordovaInterface {
|
||||
}
|
||||
|
||||
/**
|
||||
* Routes the result to the awaiting plugin. Returns false if no plugin was waiting.
|
||||
* Routes the result to the awaiting plugin.
|
||||
*
|
||||
* @return false if no plugin was waiting.
|
||||
*/
|
||||
public boolean onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
CordovaPlugin callback = activityResultCallback;
|
||||
@@ -223,18 +225,21 @@ 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)
|
||||
{
|
||||
return PackageManager.PERMISSION_GRANTED == activity.checkSelfPermission(permission);
|
||||
|
||||
@@ -64,7 +64,11 @@ public class CordovaPlugin {
|
||||
* Called after plugin construction and fields have been initialized.
|
||||
* Prefer to use pluginInitialize instead since there is no value in
|
||||
* having parameters on the initialize() function.
|
||||
*
|
||||
* @deprecated Use {@link #pluginInitialize()} instead. This method is no longer recommended
|
||||
* and will be removed in future versions.
|
||||
*/
|
||||
@Deprecated
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
}
|
||||
|
||||
@@ -75,7 +79,7 @@ public class CordovaPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the plugin's service name (what you'd use when calling pluginManger.getPlugin())
|
||||
* @return the plugin's service name (what you'd use when calling pluginManger.getPlugin())
|
||||
*/
|
||||
public String getServiceName() {
|
||||
return serviceName;
|
||||
@@ -84,11 +88,14 @@ public class CordovaPlugin {
|
||||
/**
|
||||
* Executes the request.
|
||||
*
|
||||
* This method is called from the WebView thread. To do a non-trivial amount of work, use:
|
||||
* cordova.getThreadPool().execute(runnable);
|
||||
* <p>This method is called from the WebView thread. To do a non-trivial
|
||||
* amount of work, use:</p>
|
||||
*
|
||||
* To run on the UI thread, use:
|
||||
* cordova.getActivity().runOnUiThread(runnable);
|
||||
* <pre>cordova.getThreadPool().execute(runnable);</pre>
|
||||
*
|
||||
* <p>To run on the UI thread, use:</p>
|
||||
*
|
||||
* <pre>cordova.getActivity().runOnUiThread(runnable);</pre>
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param rawArgs The exec() arguments in JSON form.
|
||||
@@ -103,11 +110,13 @@ public class CordovaPlugin {
|
||||
/**
|
||||
* Executes the request.
|
||||
*
|
||||
* This method is called from the WebView thread. To do a non-trivial amount of work, use:
|
||||
* cordova.getThreadPool().execute(runnable);
|
||||
* <p>This method is called from the WebView thread. To do a non-trivial amount of work, use:</p>
|
||||
*
|
||||
* To run on the UI thread, use:
|
||||
* cordova.getActivity().runOnUiThread(runnable);
|
||||
* <pre>cordova.getThreadPool().execute(runnable);</pre>
|
||||
*
|
||||
* <p>To run on the UI thread, use:</p>
|
||||
*
|
||||
* <pre>cordova.getActivity().runOnUiThread(runnable);</pre>
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args The exec() arguments.
|
||||
@@ -122,10 +131,10 @@ public class CordovaPlugin {
|
||||
/**
|
||||
* Executes the request.
|
||||
*
|
||||
* This method is called from the WebView thread. To do a non-trivial amount of work, use:
|
||||
* <p>This method is called from the WebView thread. To do a non-trivial amount of work, use:</p>
|
||||
* cordova.getThreadPool().execute(runnable);
|
||||
*
|
||||
* To run on the UI thread, use:
|
||||
* <p>To run on the UI thread, use:</p>
|
||||
* cordova.getActivity().runOnUiThread(runnable);
|
||||
*
|
||||
* @param action The action to execute.
|
||||
@@ -227,18 +236,18 @@ public class CordovaPlugin {
|
||||
/**
|
||||
* Hook for blocking the loading of external resources.
|
||||
*
|
||||
* This will be called when the WebView's shouldInterceptRequest wants to
|
||||
* <p>This will be called when the WebView's shouldInterceptRequest wants to
|
||||
* know whether to open a connection to an external resource. Return false
|
||||
* to block the request: if any plugin returns false, Cordova will block
|
||||
* the request. If all plugins return null, the default policy will be
|
||||
* enforced. If at least one plugin returns true, and no plugins return
|
||||
* false, then the request will proceed.
|
||||
* false, then the request will proceed.</p>
|
||||
*
|
||||
* Note that this only affects resource requests which are routed through
|
||||
* <p>Note that this only affects resource requests which are routed through
|
||||
* WebViewClient.shouldInterceptRequest, such as XMLHttpRequest requests and
|
||||
* img tag loads. WebSockets and media requests (such as <video> and <audio>
|
||||
* tags) are not affected by this method. Use CSP headers to control access
|
||||
* to such resources.
|
||||
* to such resources.</p>
|
||||
*/
|
||||
public Boolean shouldAllowRequest(String url) {
|
||||
return null;
|
||||
@@ -246,13 +255,13 @@ public class CordovaPlugin {
|
||||
|
||||
/**
|
||||
* Hook for blocking navigation by the Cordova WebView. This applies both to top-level and
|
||||
* iframe navigations.
|
||||
* iframe navigation.
|
||||
*
|
||||
* This will be called when the WebView's needs to know whether to navigate
|
||||
* <p>This will be called when the WebView's needs to know whether to navigate
|
||||
* to a new page. Return false to block the navigation: if any plugin
|
||||
* returns false, Cordova will block the navigation. If all plugins return
|
||||
* null, the default policy will be enforced. It at least one plugin returns
|
||||
* true, and no plugins return false, then the navigation will proceed.
|
||||
* true, and no plugins return false, then the navigation will proceed.</p>
|
||||
*/
|
||||
public Boolean shouldAllowNavigation(String url) {
|
||||
return null;
|
||||
@@ -270,12 +279,12 @@ public class CordovaPlugin {
|
||||
/**
|
||||
* Hook for blocking the launching of Intents by the Cordova application.
|
||||
*
|
||||
* This will be called when the WebView will not navigate to a page, but
|
||||
* <p>This will be called when the WebView will not navigate to a page, but
|
||||
* could launch an intent to handle the URL. Return false to block this: if
|
||||
* any plugin returns false, Cordova will block the navigation. If all
|
||||
* plugins return null, the default policy will be enforced. If at least one
|
||||
* plugin returns true, and no plugins return false, then the URL will be
|
||||
* opened.
|
||||
* opened.</p>
|
||||
*/
|
||||
public Boolean shouldOpenExternalUrl(String url) {
|
||||
return null;
|
||||
@@ -284,8 +293,8 @@ public class CordovaPlugin {
|
||||
/**
|
||||
* Allows plugins to handle a link being clicked. Return true here to cancel the navigation.
|
||||
*
|
||||
* @param url The URL that is trying to be loaded in the Cordova webview.
|
||||
* @return Return true to prevent the URL from loading. Default is false.
|
||||
* @param url The URL that is trying to be loaded in the Cordova WebView.
|
||||
* @return true to prevent the URL from loading. Default is false.
|
||||
*/
|
||||
public boolean onOverrideUrlLoading(String url) {
|
||||
return false;
|
||||
@@ -295,17 +304,20 @@ public class CordovaPlugin {
|
||||
* Hook for redirecting requests. Applies to WebView requests as well as requests made by plugins.
|
||||
* To handle the request directly, return a URI in the form:
|
||||
*
|
||||
* cdvplugin://pluginId/...
|
||||
* <pre>cdvplugin://pluginId/...</pre>
|
||||
*
|
||||
* And implement handleOpenForRead().
|
||||
* To make this easier, use the toPluginUri() and fromPluginUri() helpers:
|
||||
* <p>And implement handleOpenForRead().</p>
|
||||
*
|
||||
* <p>To make this easier, use the toPluginUri() and fromPluginUri() helpers:</p>
|
||||
*
|
||||
* <pre>
|
||||
* public Uri remapUri(Uri uri) { return toPluginUri(uri); }
|
||||
*
|
||||
* public CordovaResourceApi.OpenForReadResult handleOpenForRead(Uri uri) throws IOException {
|
||||
* Uri origUri = fromPluginUri(uri);
|
||||
* ...
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public Uri remapUri(Uri uri) {
|
||||
return null;
|
||||
@@ -343,9 +355,9 @@ public class CordovaPlugin {
|
||||
/**
|
||||
* Called when the WebView does a top-level navigation or refreshes.
|
||||
*
|
||||
* Plugins should stop any long-running processes and clean up internal state.
|
||||
* <p>Plugins should stop any long-running processes and clean up internal state.</p>
|
||||
*
|
||||
* Does nothing by default.
|
||||
* <p>Does nothing by default.</p>
|
||||
*/
|
||||
public void onReset() {
|
||||
}
|
||||
@@ -358,9 +370,7 @@ public class CordovaPlugin {
|
||||
* @param handler The HttpAuthHandler used to set the WebView's response
|
||||
* @param host The host requiring authentication
|
||||
* @param realm The realm for which authentication is required
|
||||
*
|
||||
* @return Returns True if plugin will resolve this auth challenge, otherwise False
|
||||
*
|
||||
* @return true if the plugin will resolve this auth challenge, else false
|
||||
*/
|
||||
public boolean onReceivedHttpAuthRequest(CordovaWebView view, ICordovaHttpAuthHandler handler, String host, String realm) {
|
||||
return false;
|
||||
@@ -372,9 +382,7 @@ public class CordovaPlugin {
|
||||
*
|
||||
* @param view The WebView that is initiating the callback
|
||||
* @param request The client certificate request
|
||||
*
|
||||
* @return Returns True if plugin will resolve this auth challenge, otherwise False
|
||||
*
|
||||
* @return True if plugin will resolve this auth challenge, otherwise False
|
||||
*/
|
||||
public boolean onReceivedClientCertRequest(CordovaWebView view, ICordovaClientCertRequest request) {
|
||||
return false;
|
||||
@@ -392,20 +400,17 @@ public class CordovaPlugin {
|
||||
* Called by the Plugin Manager when we need to actually request permissions
|
||||
*
|
||||
* @param requestCode Passed to the activity to track the request
|
||||
*
|
||||
* @return Returns the permission that was stored in the plugin
|
||||
* @return The permission that was stored in the plugin
|
||||
*/
|
||||
|
||||
public void requestPermissions(int requestCode) {
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Called by the WebView implementation to check for geolocation permissions, can be used
|
||||
* by other Java methods in the event that a plugin is using this as a dependency.
|
||||
*
|
||||
* @return Returns true if the plugin has all the permissions it needs to operate.
|
||||
* @return True if the plugin has all the permissions it needs to operate.
|
||||
*/
|
||||
|
||||
public boolean hasPermisssion() {
|
||||
return true;
|
||||
}
|
||||
@@ -416,7 +421,6 @@ public class CordovaPlugin {
|
||||
* @param requestCode
|
||||
* @param permissions
|
||||
* @param grantResults
|
||||
*
|
||||
* @deprecated Use {@link #onRequestPermissionsResult} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@@ -439,6 +443,7 @@ public class CordovaPlugin {
|
||||
|
||||
/**
|
||||
* Allow plugins to supply a PathHandler for the WebViewAssetHandler
|
||||
*
|
||||
* @return a CordovaPluginPathHandler which listen for paths and returns a response
|
||||
*/
|
||||
public CordovaPluginPathHandler getPathHandler() {
|
||||
@@ -446,11 +451,13 @@ public class CordovaPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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>
|
||||
* <p>See <a href="https://developer.android.com/reference/android/webkit/WebViewClient#onRenderProcessGone(android.webkit.WebView,%20android.webkit.RenderProcessGoneDetail)">WebViewClient#onRenderProcessGone</a></p>
|
||||
*
|
||||
* Note: A plugin must not attempt to recover a webview that it does not own/manage.
|
||||
* <p>Note: A plugin must not attempt to recover a WebView that it does not own/manage.</p>
|
||||
*
|
||||
* @return true if the host application handled the situation that process has exited,
|
||||
* otherwise, application will crash if render process crashed, or be killed
|
||||
|
||||
@@ -45,21 +45,36 @@ import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* What this class provides:
|
||||
* 1. Helpers for reading & writing to URLs.
|
||||
* - E.g. handles assets, resources, content providers, files, data URIs, http[s]
|
||||
* - E.g. Can be used to query for mime-type & content length.
|
||||
*
|
||||
* 2. To allow plugins to redirect URLs (via remapUrl).
|
||||
* - All plugins should call remapUrl() on URLs they receive from JS *before*
|
||||
* passing the URL onto other utility functions in this class.
|
||||
* - For an example usage of this, refer to the org.apache.cordova.file plugin.
|
||||
* <ol>
|
||||
* <li>
|
||||
* Helpers for reading & writing to URLs.
|
||||
* <ul>
|
||||
* <li>E.g. handles assets, resources, content providers, files, data URIs, http[s]</li>
|
||||
* <li>E.g. Can be used to query for mime-type & content length.</p></li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li>
|
||||
* To allow plugins to redirect URLs (via remapUrl).
|
||||
* <ul>
|
||||
* <li>
|
||||
* All plugins should call remapUrl() on URLs they receive from JS *before* passing the URL onto other utility functions in this class.
|
||||
* </li>
|
||||
* <li>For an example usage of this, refer to the org.apache.cordova.file plugin.</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ol>
|
||||
*
|
||||
* Future Work:
|
||||
* - Consider using a Cursor to query content URLs for their size (like the file plugin does).
|
||||
* - Allow plugins to remapUri to "cdv-plugin://plugin-name/foo", which CordovaResourceApi
|
||||
* would then delegate to pluginManager.getPlugin(plugin-name).openForRead(url)
|
||||
* - Currently, plugins *can* do this by remapping to a data: URL, but it's inefficient
|
||||
* for large payloads.
|
||||
* <p>Future Work:</p>
|
||||
* <ul>
|
||||
* <li>Consider using a Cursor to query content URLs for their size (like the file plugin does).</li>
|
||||
* <li>
|
||||
* Allow plugins to remapUri to "cdv-plugin://plugin-name/foo", which CordovaResourceApi would then delegate to pluginManager.getPlugin(plugin-name).openForRead(url)
|
||||
* <ul>
|
||||
* <li>Currently, plugins *can* do this by remapping to a data: URL, but it's inefficient for large payloads.</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ul>
|
||||
*/
|
||||
public class CordovaResourceApi {
|
||||
@SuppressWarnings("unused")
|
||||
@@ -143,8 +158,7 @@ public class CordovaResourceApi {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a File that points to the resource, or null if the resource
|
||||
* is not on the local filesystem.
|
||||
* @return A file that points to the resource, or null if the resource is not on the local filesystem.
|
||||
*/
|
||||
public File mapUriToFile(Uri uri) {
|
||||
assertBackgroundThread();
|
||||
@@ -223,11 +237,12 @@ public class CordovaResourceApi {
|
||||
|
||||
/**
|
||||
* Opens a stream to the given URI, also providing the MIME type & length.
|
||||
*
|
||||
* @return Never returns null.
|
||||
* @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be
|
||||
* resolved before being passed into this function.
|
||||
* @throws Throws an IOException if the URI cannot be opened.
|
||||
* @throws Throws an IllegalStateException if called on a foreground thread.
|
||||
* @throws IllegalArgumentException For relative URIs. Relative URIs should be resolved before
|
||||
* being passed into this function.
|
||||
* @throws IOException If the URI cannot be opened.
|
||||
* @throws IllegalStateException If called on a foreground thread.
|
||||
*/
|
||||
public OpenForReadResult openForRead(Uri uri) throws IOException {
|
||||
return openForRead(uri, false);
|
||||
@@ -235,11 +250,12 @@ public class CordovaResourceApi {
|
||||
|
||||
/**
|
||||
* Opens a stream to the given URI, also providing the MIME type & length.
|
||||
*
|
||||
* @return Never returns null.
|
||||
* @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be
|
||||
* resolved before being passed into this function.
|
||||
* @throws Throws an IOException if the URI cannot be opened.
|
||||
* @throws Throws an IllegalStateException if called on a foreground thread and skipThreadCheck is false.
|
||||
* @throws IllegalArgumentException For relative URIs. Relative URIs should be resolved before
|
||||
* being passed into this function.
|
||||
* @throws IOException If the URI cannot be opened.
|
||||
* @throws IllegalStateException If called on a foreground thread and skipThreadCheck is false.
|
||||
*/
|
||||
public OpenForReadResult openForRead(Uri uri, boolean skipThreadCheck) throws IOException {
|
||||
if (!skipThreadCheck) {
|
||||
@@ -320,10 +336,11 @@ public class CordovaResourceApi {
|
||||
|
||||
/**
|
||||
* Opens a stream to the given URI.
|
||||
*
|
||||
* @return Never returns null.
|
||||
* @throws Throws an InvalidArgumentException for relative URIs. Relative URIs should be
|
||||
* resolved before being passed into this function.
|
||||
* @throws Throws an IOException if the URI cannot be opened.
|
||||
* @throws IllegalArgumentException For relative URIs. Relative URIs should be resolved before
|
||||
* being passed into this function.
|
||||
* @throws IOException If the URI cannot be opened.
|
||||
*/
|
||||
public OutputStream openOutputStream(Uri uri, boolean append) throws IOException {
|
||||
assertBackgroundThread();
|
||||
|
||||
@@ -25,13 +25,13 @@ import android.view.View;
|
||||
import android.webkit.WebChromeClient.CustomViewCallback;
|
||||
|
||||
/**
|
||||
* Main interface for interacting with a Cordova webview - implemented by CordovaWebViewImpl.
|
||||
* Main interface for interacting with a Cordova WebView - implemented by CordovaWebViewImpl.
|
||||
* This is an interface so that it can be easily mocked in tests.
|
||||
* Methods may be added to this interface without a major version bump, as plugins & embedders
|
||||
* Methods may be added to this interface without a major version bump, as plugins/developer
|
||||
* are not expected to implement it.
|
||||
*/
|
||||
public interface CordovaWebView {
|
||||
public static final String CORDOVA_VERSION = "12.0.0";
|
||||
public static final String CORDOVA_VERSION = "15.0.0-dev";
|
||||
|
||||
void init(CordovaInterface cordova, List<PluginEntry> pluginEntries, CordovaPreferences preferences);
|
||||
|
||||
@@ -70,34 +70,47 @@ public interface CordovaWebView {
|
||||
/**
|
||||
* Send JavaScript statement back to JavaScript.
|
||||
*
|
||||
* Deprecated (https://issues.apache.org/jira/browse/CB-6851)
|
||||
* <p>Deprecated (<a href="https://issues.apache.org/jira/browse/CB-6851">CB-6851</a>)
|
||||
* Instead of executing snippets of JS, you should use the exec bridge
|
||||
* to create a Java->JS communication channel.
|
||||
* To do this:
|
||||
* 1. Within plugin.xml (to have your JS run before deviceready):
|
||||
* <js-module><runs/></js-module>
|
||||
* 2. Within your .js (call exec on start-up):
|
||||
* to create a Java->JS communication channel.</p>
|
||||
*
|
||||
* <p>To do this:</p>
|
||||
*
|
||||
* <p>1. Within plugin.xml (to have your JS run before deviceready):</p>
|
||||
*
|
||||
* <pre>
|
||||
* <js-module><runs/></js-module>
|
||||
* </pre>
|
||||
*
|
||||
* <p>2. Within your .js (call exec on start-up):</p>
|
||||
*
|
||||
* <pre>
|
||||
* require('cordova/channel').onCordovaReady.subscribe(function() {
|
||||
* require('cordova/exec')(win, null, 'Plugin', 'method', []);
|
||||
* function win(message) {
|
||||
* ... process message from java here ...
|
||||
* }
|
||||
* });
|
||||
* 3. Within your .java:
|
||||
* </pre>
|
||||
*
|
||||
* <p>3. Within your .java:</p>
|
||||
*
|
||||
* <pre>
|
||||
* PluginResult dataResult = new PluginResult(PluginResult.Status.OK, CODE);
|
||||
* dataResult.setKeepCallback(true);
|
||||
* savedCallbackContext.sendPluginResult(dataResult);
|
||||
* </pre>
|
||||
*/
|
||||
@Deprecated
|
||||
void sendJavascript(String statememt);
|
||||
|
||||
/**
|
||||
* Load the specified URL in the Cordova webview or a new browser instance.
|
||||
* Load the specified URL in the Cordova WebView or a new browser instance.
|
||||
*
|
||||
* NOTE: If openExternal is false, only allow listed URLs can be loaded.
|
||||
* <p>NOTE: If openExternal is false, only allow listed URLs can be loaded.</p>
|
||||
*
|
||||
* @param url The url to load.
|
||||
* @param openExternal Load url in browser instead of Cordova webview.
|
||||
* @param openExternal Load url in browser instead of Cordova WebView.
|
||||
* @param clearHistory Clear the history stack, so new page becomes top of history
|
||||
* @param params Parameters for new app
|
||||
*/
|
||||
|
||||
@@ -59,7 +59,7 @@ public interface CordovaWebViewEngine {
|
||||
/** Clean up all resources associated with the WebView. */
|
||||
void destroy();
|
||||
|
||||
/** Add the evaulate Javascript method **/
|
||||
/** Add the evaluate Javascript method **/
|
||||
void evaluateJavascript(String js, ValueCallback<String> callback);
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,7 +43,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Main class for interacting with a Cordova webview. Manages plugins, events, and a CordovaWebViewEngine.
|
||||
* Main class for interacting with a Cordova WebView. Manages plugins, events, and a CordovaWebViewEngine.
|
||||
* Class uses two-phase initialization. You must call init() before calling any other methods.
|
||||
*/
|
||||
public class CordovaWebViewImpl implements CordovaWebView {
|
||||
@@ -149,11 +149,12 @@ public class CordovaWebViewImpl implements CordovaWebView {
|
||||
|
||||
// Timeout error method
|
||||
final Runnable loadError = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
stopLoading();
|
||||
LOG.e(TAG, "CordovaWebView: TIMEOUT ERROR!");
|
||||
|
||||
// Handle other errors by passing them to the webview in JS
|
||||
// Handle other errors by passing them to the WebView in JS
|
||||
JSONObject data = new JSONObject();
|
||||
try {
|
||||
data.put("errorCode", -6);
|
||||
@@ -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);
|
||||
@@ -216,7 +219,7 @@ public class CordovaWebViewImpl implements CordovaWebView {
|
||||
engine.clearHistory();
|
||||
}
|
||||
|
||||
// If loading into our webview
|
||||
// If loading into our WebView
|
||||
if (!openExternal) {
|
||||
// Make sure url is in allow list
|
||||
if (pluginManager.shouldAllowNavigation(url)) {
|
||||
@@ -484,7 +487,7 @@ public class CordovaWebViewImpl implements CordovaWebView {
|
||||
|
||||
// If app doesn't want to run in background
|
||||
if (!keepRunning) {
|
||||
// Pause JavaScript timers. This affects all webviews within the app!
|
||||
// Pause JavaScript timers. This affects all WebViews within the app!
|
||||
engine.setPaused(true);
|
||||
}
|
||||
}
|
||||
@@ -494,7 +497,7 @@ public class CordovaWebViewImpl implements CordovaWebView {
|
||||
return;
|
||||
}
|
||||
|
||||
// Resume JavaScript timers. This affects all webviews within the app!
|
||||
// Resume JavaScript timers. This affects all WebViews within the app!
|
||||
engine.setPaused(false);
|
||||
this.pluginManager.onResume(keepRunning);
|
||||
|
||||
@@ -534,7 +537,7 @@ public class CordovaWebViewImpl implements CordovaWebView {
|
||||
// We should use a blank data: url instead so it's more obvious
|
||||
this.loadUrl("about:blank");
|
||||
|
||||
// TODO: Should not destroy webview until after about:blank is done loading.
|
||||
// TODO: Should not destroy WebView until after about:blank is done loading.
|
||||
engine.destroy();
|
||||
hideCustomView();
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -28,9 +28,12 @@ import org.json.JSONObject;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.content.IntentFilter;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.view.KeyEvent;
|
||||
import android.window.OnBackInvokedCallback;
|
||||
import android.window.OnBackInvokedDispatcher;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@@ -45,7 +48,9 @@ public class CoreAndroid extends CordovaPlugin {
|
||||
private CallbackContext messageChannel;
|
||||
private PluginResult pendingResume;
|
||||
private PluginResult pendingPause;
|
||||
private OnBackInvokedCallback backCallback;
|
||||
private final Object messageChannelLock = new Object();
|
||||
private final Object backButtonHandlerLock = new Object();
|
||||
|
||||
/**
|
||||
* Send an event to be fired on the Javascript side.
|
||||
@@ -63,16 +68,19 @@ public class CoreAndroid extends CordovaPlugin {
|
||||
@Override
|
||||
public void pluginInitialize() {
|
||||
this.initTelephonyReceiver();
|
||||
backCallback = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param args JSONArray of arguments for the plugin.
|
||||
* @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 = "";
|
||||
@@ -82,10 +90,11 @@ public class CoreAndroid extends CordovaPlugin {
|
||||
this.clearCache();
|
||||
}
|
||||
else if (action.equals("show")) {
|
||||
// This gets called from JavaScript onCordovaReady to show the webview.
|
||||
// This gets called from JavaScript onCordovaReady to show the WebView.
|
||||
// I recommend we change the name of the Message as spinner/stop is not
|
||||
// indicative of what this actually does (shows the webview).
|
||||
// indicative of what this actually does (shows the WebView).
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
webView.getPluginManager().postMessage("spinner", "stop");
|
||||
}
|
||||
@@ -144,6 +153,7 @@ public class CoreAndroid extends CordovaPlugin {
|
||||
*/
|
||||
public void clearCache() {
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
webView.clearCache();
|
||||
}
|
||||
@@ -151,7 +161,7 @@ public class CoreAndroid extends CordovaPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the url into the webview.
|
||||
* Load the url into the WebView.
|
||||
*
|
||||
* @param url
|
||||
* @param props Properties that can be passed in to the Cordova activity (i.e. loadingDialog, wait, ...)
|
||||
@@ -215,6 +225,7 @@ public class CoreAndroid extends CordovaPlugin {
|
||||
*/
|
||||
public void clearHistory() {
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
webView.clearHistory();
|
||||
}
|
||||
@@ -227,6 +238,7 @@ public class CoreAndroid extends CordovaPlugin {
|
||||
*/
|
||||
public void backHistory() {
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
webView.backHistory();
|
||||
}
|
||||
@@ -241,6 +253,29 @@ public class CoreAndroid extends CordovaPlugin {
|
||||
*/
|
||||
public void overrideBackbutton(boolean override) {
|
||||
LOG.i("App", "WARNING: Back Button Default Behavior will be overridden. The backbutton event will be fired!");
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) {
|
||||
if (override) {
|
||||
synchronized (backButtonHandlerLock) {
|
||||
if (backCallback == null) {
|
||||
// The callback is intentionally empty. Since API 36, intercepting the back button is ignored, which means
|
||||
// the onDispatchKeyEvent boolean result won't actually stop native from consuming the back button and doing
|
||||
// it's own logic, UNLESS if there is an OnBackInvokedCallback registered on the dispatcher.
|
||||
// The key dispatch events will still fire, which still handles propagating back button events to the webview.
|
||||
// See https://developer.android.com/about/versions/16/behavior-changes-16#predictive-back for more info on the caveat.
|
||||
backCallback = () -> {};
|
||||
this.cordova.getActivity().getOnBackInvokedDispatcher().registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT, backCallback);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
synchronized (backButtonHandlerLock) {
|
||||
if (backCallback != null) {
|
||||
this.cordova.getActivity().getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(backCallback);
|
||||
backCallback = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_BACK, override);
|
||||
}
|
||||
|
||||
@@ -349,10 +384,10 @@ public class CoreAndroid extends CordovaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Unregister the receiver
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void onDestroy()
|
||||
{
|
||||
webView.getContext().unregisterReceiver(this.telephonyReceiver);
|
||||
|
||||
@@ -31,32 +31,32 @@ public interface ICordovaClientCertRequest {
|
||||
*/
|
||||
public void cancel();
|
||||
|
||||
/*
|
||||
* Returns the host name of the server requesting the certificate.
|
||||
/**
|
||||
* @return the host name of the server requesting the certificate.
|
||||
*/
|
||||
public String getHost();
|
||||
|
||||
/*
|
||||
* Returns the acceptable types of asymmetric keys (can be null).
|
||||
/**
|
||||
* @return the acceptable types of asymmetric keys (can be null).
|
||||
*/
|
||||
public String[] getKeyTypes();
|
||||
|
||||
/*
|
||||
* Returns the port number of the server requesting the certificate.
|
||||
/**
|
||||
* @return the port number of the server requesting the certificate.
|
||||
*/
|
||||
public int getPort();
|
||||
|
||||
/*
|
||||
* Returns the acceptable certificate issuers for the certificate matching the private key (can be null).
|
||||
/**
|
||||
* @return the acceptable certificate issuers for the certificate matching the private key (can be null).
|
||||
*/
|
||||
public Principal[] getPrincipals();
|
||||
|
||||
/*
|
||||
/**
|
||||
* Ignore the request for now. Do not remember user's choice.
|
||||
*/
|
||||
public void ignore();
|
||||
|
||||
/*
|
||||
/**
|
||||
* Proceed with the specified private key and client certificate chain. Remember the user's positive choice and use it for future requests.
|
||||
*
|
||||
* @param privateKey The privateKey
|
||||
|
||||
@@ -23,8 +23,8 @@ import android.util.Log;
|
||||
/**
|
||||
* Log to Android logging system.
|
||||
*
|
||||
* Log message can be a string or a printf formatted string with arguments.
|
||||
* See http://developer.android.com/reference/java/util/Formatter.html
|
||||
* <p>Log message can be a string or a printf formatted string with arguments.
|
||||
* See <a href="http://developer.android.com/reference/java/util/Formatter.html">Formatter</a></p>
|
||||
*/
|
||||
public class LOG {
|
||||
|
||||
|
||||
@@ -124,12 +124,10 @@ public class NativeToJsMessageQueue {
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines and returns queued messages combined into a single string.
|
||||
*
|
||||
* Combines as many messages as possible, without exceeding
|
||||
* COMBINED_RESPONSE_CUTOFF in case of multiple response messages.
|
||||
*
|
||||
* Returns null if the queue is empty.
|
||||
* @return a string of queued messages combined or null if the queue is empty.
|
||||
*/
|
||||
public String popAndEncode(boolean fromOnlineEvent) {
|
||||
synchronized (this) {
|
||||
@@ -206,7 +204,7 @@ public class NativeToJsMessageQueue {
|
||||
}
|
||||
}
|
||||
if (!willSendAllMessages) {
|
||||
sb.append("window.setTimeout(function(){cordova.require('cordova/plugin/android/polling').pollOnce();},0);");
|
||||
sb.append("window.setTimeout(function(){cordova.require('cordova/exec').pollOnce();},0);");
|
||||
}
|
||||
for (int i = willSendAllMessages ? 1 : 0; i < numMessagesToSend; ++i) {
|
||||
sb.append('}');
|
||||
@@ -302,6 +300,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 +329,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 +342,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 +373,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) {
|
||||
|
||||
@@ -66,7 +66,6 @@ public class PermissionHelper {
|
||||
*
|
||||
* @param plugin The plugin the permission is being checked against
|
||||
* @param permission The permission to be checked
|
||||
*
|
||||
* @return True if the permission has already been granted and false otherwise
|
||||
*/
|
||||
public static boolean hasPermission(CordovaPlugin plugin, String permission) {
|
||||
|
||||
@@ -48,36 +48,36 @@ public final class PluginEntry {
|
||||
/**
|
||||
* Constructs with a CordovaPlugin already instantiated.
|
||||
*
|
||||
* @param service The name of the service
|
||||
* @param pluginClass The plugin class name
|
||||
* @param service The name of the service
|
||||
* @param plugin The plugin class name
|
||||
*/
|
||||
public PluginEntry(String service, CordovaPlugin plugin) {
|
||||
this(service, plugin.getClass().getName(), true, plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param service The name of the service
|
||||
* @param plugin The CordovaPlugin already instantiated
|
||||
* @param onload Create plugin object when HTML page is loaded
|
||||
* @param service The name of the service
|
||||
* @param plugin The CordovaPlugin already instantiated
|
||||
* @param onload Create plugin object when HTML page is loaded
|
||||
*/
|
||||
public PluginEntry(String service, CordovaPlugin plugin, boolean onload) {
|
||||
this(service, plugin.getClass().getName(), onload, plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param service The name of the service
|
||||
* @param pluginClass The plugin class name
|
||||
* @param onload Create plugin object when HTML page is loaded
|
||||
* @param service The name of the service
|
||||
* @param pluginClass The plugin class name
|
||||
* @param onload Create plugin object when HTML page is loaded
|
||||
*/
|
||||
public PluginEntry(String service, String pluginClass, boolean onload) {
|
||||
this(service, pluginClass, onload, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param service The name of the service
|
||||
* @param pluginClass The plugin class name
|
||||
* @param onload Create plugin object when HTML page is loaded
|
||||
* @param plugin The CordovaPlugin already instantiated
|
||||
* @param service The name of the service
|
||||
* @param pluginClass The plugin class name
|
||||
* @param onload Create plugin object when HTML page is loaded
|
||||
* @param plugin The CordovaPlugin already instantiated
|
||||
*/
|
||||
private PluginEntry(String service, String pluginClass, boolean onload, CordovaPlugin plugin) {
|
||||
this.service = service;
|
||||
|
||||
@@ -38,8 +38,8 @@ import android.webkit.WebView;
|
||||
/**
|
||||
* PluginManager is exposed to JavaScript in the Cordova WebView.
|
||||
*
|
||||
* Calling native plugin code can be done by calling PluginManager.exec(...)
|
||||
* from JavaScript.
|
||||
* <p>Calling native plugin code can be done by calling PluginManager.exec(...)
|
||||
* from JavaScript.</p>
|
||||
*/
|
||||
public class PluginManager {
|
||||
private static String TAG = "PluginManager";
|
||||
@@ -87,7 +87,7 @@ public class PluginManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Init when loading a new HTML page into webview.
|
||||
* Init when loading a new HTML page into WebView.
|
||||
*/
|
||||
public void init() {
|
||||
LOG.d(TAG, "init()");
|
||||
@@ -121,9 +121,9 @@ public class PluginManager {
|
||||
* Receives a request for execution and fulfills it by finding the appropriate
|
||||
* Java class and calling it's execute method.
|
||||
*
|
||||
* PluginManager.exec can be used either synchronously or async. In either case, a JSON encoded
|
||||
* <p>PluginManager.exec can be used either synchronously or async. In either case, a JSON encoded
|
||||
* string is returned that will indicate if any errors have occurred when trying to find
|
||||
* or execute the class denoted by the clazz argument.
|
||||
* or execute the class denoted by the clazz argument.</p>
|
||||
*
|
||||
* @param service String containing the service to run
|
||||
* @param action String containing the action that the class is supposed to perform. This is
|
||||
@@ -252,9 +252,7 @@ public class PluginManager {
|
||||
* @param handler The HttpAuthHandler used to set the WebView's response
|
||||
* @param host The host requiring authentication
|
||||
* @param realm The realm for which authentication is required
|
||||
*
|
||||
* @return Returns True if there is a plugin which will resolve this auth challenge, otherwise False
|
||||
*
|
||||
* @return True if there is a plugin which will resolve this auth challenge, otherwise False
|
||||
*/
|
||||
public boolean onReceivedHttpAuthRequest(CordovaWebView view, ICordovaHttpAuthHandler handler, String host, String realm) {
|
||||
synchronized (this.pluginMap) {
|
||||
@@ -273,9 +271,7 @@ public class PluginManager {
|
||||
*
|
||||
* @param view The WebView that is initiating the callback
|
||||
* @param request The client certificate request
|
||||
*
|
||||
* @return Returns True if plugin will resolve this auth challenge, otherwise False
|
||||
*
|
||||
* @return True if plugin will resolve this auth challenge, otherwise False
|
||||
*/
|
||||
public boolean onReceivedClientCertRequest(CordovaWebView view, ICordovaClientCertRequest request) {
|
||||
synchronized (this.pluginMap) {
|
||||
@@ -375,12 +371,14 @@ public class PluginManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo should we move this somewhere public and accessible by all plugins?
|
||||
* For now, it is placed where it is used and kept private so we can decide later and move without causing a breaking change.
|
||||
* An ideal location might be in the "ConfigXmlParser" at the time it generates the "launchUrl".
|
||||
* TODO: should we move this somewhere public and accessible by all plugins?
|
||||
*
|
||||
* @todo should we be restrictive on the "file://" return? e.g. "file:///android_asset/www/"
|
||||
* Would be considered as a breaking change if we apply a more granular check.
|
||||
* <p>For now, it is placed where it is used and kept private so we can decide later and move without causing a breaking change.
|
||||
* An ideal location might be in the "ConfigXmlParser" at the time it generates the "launchUrl".</p>
|
||||
*
|
||||
* TODO: should we be restrictive on the "file://" return? e.g. "file:///android_asset/www/"
|
||||
*
|
||||
* <p>Would be considered as a breaking change if we apply a more granular check.</p>
|
||||
*/
|
||||
private String getLaunchUrlPrefix() {
|
||||
if (!app.getPreferences().getBoolean("AndroidInsecureFileModeEnabled", false)) {
|
||||
@@ -393,14 +391,14 @@ public class PluginManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the webview is going to request an external resource.
|
||||
* Called when the WebView is going to request an external resource.
|
||||
*
|
||||
* This delegates to the installed plugins, and returns true/false for the
|
||||
* <p>This delegates to the installed plugins, and returns true/false for the
|
||||
* first plugin to provide a non-null result. If no plugins respond, then
|
||||
* the default policy is applied.
|
||||
* the default policy is applied.</p>
|
||||
*
|
||||
* @param url The URL that is being requested.
|
||||
* @return Returns true to allow the resource to load,
|
||||
* @return true to allow the resource to load,
|
||||
* false to block the resource.
|
||||
*/
|
||||
public boolean shouldAllowRequest(String url) {
|
||||
@@ -425,7 +423,7 @@ public class PluginManager {
|
||||
return true;
|
||||
}
|
||||
if (url.startsWith("file://")) {
|
||||
//This directory on WebKit/Blink based webviews contains SQLite databases!
|
||||
//This directory on WebKit/Blink based WebViews contains SQLite databases!
|
||||
//DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING!
|
||||
return !url.contains("/app_webview/");
|
||||
}
|
||||
@@ -433,14 +431,14 @@ public class PluginManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the webview is going to change the URL of the loaded content.
|
||||
* Called when the WebView is going to change the URL of the loaded content.
|
||||
*
|
||||
* This delegates to the installed plugins, and returns true/false for the
|
||||
* <p>This delegates to the installed plugins, and returns true/false for the
|
||||
* first plugin to provide a non-null result. If no plugins respond, then
|
||||
* the default policy is applied.
|
||||
* the default policy is applied.</p>
|
||||
*
|
||||
* @param url The URL that is being requested.
|
||||
* @return Returns true to allow the navigation,
|
||||
* @return true to allow the navigation,
|
||||
* false to block the navigation.
|
||||
*/
|
||||
public boolean shouldAllowNavigation(String url) {
|
||||
@@ -462,7 +460,7 @@ public class PluginManager {
|
||||
|
||||
|
||||
/**
|
||||
* Called when the webview is requesting the exec() bridge be enabled.
|
||||
* Called when the WebView is requesting the exec() bridge be enabled.
|
||||
*/
|
||||
public boolean shouldAllowBridgeAccess(String url) {
|
||||
synchronized (this.entryMap) {
|
||||
@@ -482,15 +480,15 @@ public class PluginManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the webview is going not going to navigate, but may launch
|
||||
* Called when the WebView is going not going to navigate, but may launch
|
||||
* an Intent for an URL.
|
||||
*
|
||||
* This delegates to the installed plugins, and returns true/false for the
|
||||
* <p>This delegates to the installed plugins, and returns true/false for the
|
||||
* first plugin to provide a non-null result. If no plugins respond, then
|
||||
* the default policy is applied.
|
||||
* the default policy is applied.</p>
|
||||
*
|
||||
* @param url The URL that is being requested.
|
||||
* @return Returns true to allow the URL to launch an intent,
|
||||
* @return true to allow the URL to launch an intent,
|
||||
* false to block the intent.
|
||||
*/
|
||||
public Boolean shouldOpenExternalUrl(String url) {
|
||||
@@ -511,7 +509,7 @@ public class PluginManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the URL of the webview changes.
|
||||
* Called when the URL of the WebView changes.
|
||||
*
|
||||
* @param url The URL that is being changed to.
|
||||
* @return Return false to allow the URL to load, return true to prevent the URL from loading.
|
||||
@@ -566,7 +564,7 @@ public class PluginManager {
|
||||
c = Class.forName(className);
|
||||
}
|
||||
if (c != null & CordovaPlugin.class.isAssignableFrom(c)) {
|
||||
ret = (CordovaPlugin) c.newInstance();
|
||||
ret = (CordovaPlugin) c.getDeclaredConstructor().newInstance();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -623,9 +621,9 @@ public class PluginManager {
|
||||
/**
|
||||
* 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)
|
||||
* <p>See <a href="https://developer.android.com/reference/android/webkit/WebViewClient#onRenderProcessGone(android.webkit.WebView,%20android.webkit.RenderProcessGoneDetail)">WebViewClient#onRenderProcessGone</a></p>
|
||||
*
|
||||
* @return true if the host application handled the situation that process has exited,
|
||||
* @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.
|
||||
*/
|
||||
|
||||
@@ -118,8 +118,7 @@ public class PluginResult {
|
||||
}
|
||||
|
||||
/**
|
||||
* If messageType == MESSAGE_TYPE_STRING, then returns the message string.
|
||||
* Otherwise, returns null.
|
||||
* @return message string when messageType is MESSAGE_TYPE_STRING otherwise null.
|
||||
*/
|
||||
public String getStrMessage() {
|
||||
return strMessage;
|
||||
|
||||
@@ -45,25 +45,25 @@ public class SplashScreenPlugin extends CordovaPlugin {
|
||||
|
||||
// Config preference values
|
||||
/**
|
||||
* @param boolean autoHide to auto splash screen (default=true)
|
||||
* Boolean flag to auto hide splash screen (default=true)
|
||||
*/
|
||||
private boolean autoHide;
|
||||
/**
|
||||
* @param int delayTime in milliseconds (default=-1)
|
||||
* Integer value of how long to delay in milliseconds (default=-1)
|
||||
*/
|
||||
private int delayTime;
|
||||
/**
|
||||
* @param int fade to fade out splash screen (default=true)
|
||||
* Boolean flag if to fade to fade out splash screen (default=true)
|
||||
*/
|
||||
private boolean isFadeEnabled;
|
||||
/**
|
||||
* @param int fadeDuration fade out duration in milliseconds (default=500)
|
||||
* Integer value of the fade duration in milliseconds (default=500)
|
||||
*/
|
||||
private int fadeDuration;
|
||||
|
||||
// Internal variables
|
||||
/**
|
||||
* @param boolean keepOnScreen flag to determine if the splash screen remains visible.
|
||||
* Boolean flag to determine if the splash screen remains visible.
|
||||
*/
|
||||
private boolean keepOnScreen = true;
|
||||
|
||||
@@ -155,10 +155,13 @@ public class SplashScreenPlugin extends CordovaPlugin {
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
splashScreenViewProvider.remove();
|
||||
webView.getPluginManager().postMessage("updateSystemBars", null);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
webView.getPluginManager().postMessage("updateSystemBars", null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
374
framework/src/org/apache/cordova/SystemBarPlugin.java
Normal file
374
framework/src/org/apache/cordova/SystemBarPlugin.java
Normal file
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cordova;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
import android.view.ViewParent;
|
||||
import android.view.Window;
|
||||
import android.view.WindowInsetsController;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowCompat;
|
||||
import androidx.core.view.WindowInsetsControllerCompat;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class SystemBarPlugin extends CordovaPlugin {
|
||||
static final String PLUGIN_NAME = "SystemBarPlugin";
|
||||
|
||||
static final int INVALID_COLOR = -1;
|
||||
|
||||
// Internal variables
|
||||
private Context context;
|
||||
private Resources resources;
|
||||
private int overrideStatusBarBackgroundColor = INVALID_COLOR;
|
||||
|
||||
private boolean canEdgeToEdge = false;
|
||||
|
||||
@Override
|
||||
protected void pluginInitialize() {
|
||||
context = cordova.getContext();
|
||||
resources = context.getResources();
|
||||
canEdgeToEdge = preferences.getBoolean("AndroidEdgeToEdge", false)
|
||||
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
cordova.getActivity().runOnUiThread(this::updateSystemBars);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume(boolean multitasking) {
|
||||
super.onResume(multitasking);
|
||||
cordova.getActivity().runOnUiThread(this::updateSystemBars);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onMessage(String id, Object data) {
|
||||
if (id.equals("updateSystemBars")) {
|
||||
cordova.getActivity().runOnUiThread(this::updateSystemBars);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
if(canEdgeToEdge) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ("setStatusBarVisible".equals(action)) {
|
||||
boolean visible = args.getBoolean(0);
|
||||
cordova.getActivity().runOnUiThread(() -> setStatusBarVisible(visible));
|
||||
} else if ("setStatusBarBackgroundColor".equals(action)) {
|
||||
cordova.getActivity().runOnUiThread(() -> setStatusBarBackgroundColor(args));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
callbackContext.success();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow the app to override the status bar visibility from JS API.
|
||||
* If for some reason the statusBarView could not be discovered, it will silently ignore
|
||||
* the change request
|
||||
*
|
||||
* @param visible should the status bar be visible?
|
||||
*/
|
||||
private void setStatusBarVisible(final boolean visible) {
|
||||
View statusBar = getStatusBarView(webView);
|
||||
if (statusBar != null) {
|
||||
statusBar.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
|
||||
FrameLayout rootLayout = getRootLayout(webView);
|
||||
if (rootLayout != null) {
|
||||
ViewCompat.requestApplyInsets(rootLayout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow the app to override the status bar background color from JS API.
|
||||
* If the supplied ARGB is invalid or fails to parse, it will silently ignore
|
||||
* the change request.
|
||||
*
|
||||
* @param argbVals {A, R, G, B}
|
||||
*/
|
||||
private void setStatusBarBackgroundColor(JSONArray argbVals) {
|
||||
try {
|
||||
int a = argbVals.getInt(0);
|
||||
int r = argbVals.getInt(1);
|
||||
int g = argbVals.getInt(2);
|
||||
int b = argbVals.getInt(3);
|
||||
String hexColor = String.format("#%02X%02X%02X%02X", a, r, g, b);
|
||||
|
||||
int parsedColor = parseColorFromString(hexColor);
|
||||
if (parsedColor == INVALID_COLOR) return;
|
||||
|
||||
overrideStatusBarBackgroundColor = parsedColor;
|
||||
updateStatusBar(overrideStatusBarBackgroundColor);
|
||||
} catch (JSONException e) {
|
||||
// Silently skip
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to update all system bars (status, navigation and gesture bars) in various points
|
||||
* of the apps life cycle.
|
||||
* For example:
|
||||
* 1. Device configurations between (E.g. between dark and light mode)
|
||||
* 2. User resumes the app
|
||||
* 3. App transitions from SplashScreen Theme to App's Theme
|
||||
*/
|
||||
private void updateSystemBars() {
|
||||
// Update Root View Background Color
|
||||
int rootViewBackgroundColor = getPreferenceBackgroundColor();
|
||||
if (rootViewBackgroundColor == INVALID_COLOR) {
|
||||
rootViewBackgroundColor = canEdgeToEdge ? Color.TRANSPARENT : getUiModeColor();
|
||||
}
|
||||
updateRootView(rootViewBackgroundColor);
|
||||
|
||||
// Update StatusBar Background Color
|
||||
int statusBarBackgroundColor;
|
||||
if (overrideStatusBarBackgroundColor != INVALID_COLOR) {
|
||||
statusBarBackgroundColor = overrideStatusBarBackgroundColor;
|
||||
} else if (preferences.contains("StatusBarBackgroundColor")) {
|
||||
statusBarBackgroundColor = getPreferenceStatusBarBackgroundColor();
|
||||
} else if(preferences.contains("BackgroundColor")){
|
||||
statusBarBackgroundColor = rootViewBackgroundColor;
|
||||
} else {
|
||||
statusBarBackgroundColor = canEdgeToEdge ? Color.TRANSPARENT : getUiModeColor();
|
||||
}
|
||||
|
||||
updateStatusBar(statusBarBackgroundColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the root layout's background color with the supplied color int.
|
||||
* It will also determine if the background color is light or dark to properly adjust the
|
||||
* appearance of the navigation/gesture bar's icons so it will not clash with the background.
|
||||
* <p>
|
||||
* System bars (navigation & gesture) on SDK 25 or lower is forced to black as the appearance
|
||||
* of the fonts can not be updated.
|
||||
* System bars (navigation & gesture) on SDK 26 or greater allows custom background color.
|
||||
* <p/>
|
||||
*
|
||||
* @param bgColor Background color
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
private void updateRootView(int bgColor) {
|
||||
Window window = cordova.getActivity().getWindow();
|
||||
|
||||
// Set the root view's background color. Works on SDK 36+
|
||||
View root = cordova.getActivity().findViewById(android.R.id.content);
|
||||
if (root != null) root.setBackgroundColor(bgColor);
|
||||
|
||||
// Automatically set the font and icon color of the system bars based on background color.
|
||||
boolean isBackgroundColorLight;
|
||||
if(bgColor == Color.TRANSPARENT) {
|
||||
isBackgroundColorLight = isColorLight(getUiModeColor());
|
||||
} else {
|
||||
isBackgroundColorLight = isColorLight(bgColor);
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
WindowInsetsController controller = window.getInsetsController();
|
||||
if (controller != null) {
|
||||
int appearance = WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
|
||||
if (isBackgroundColorLight) {
|
||||
controller.setSystemBarsAppearance(0, appearance);
|
||||
} else {
|
||||
controller.setSystemBarsAppearance(appearance, appearance);
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowInsetsControllerCompat controllerCompat = WindowCompat.getInsetsController(window, window.getDecorView());
|
||||
controllerCompat.setAppearanceLightNavigationBars(isBackgroundColorLight);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
window.setNavigationBarColor(bgColor);
|
||||
} else {
|
||||
window.setNavigationBarColor(Color.BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the statusBarView background color with the supplied color int.
|
||||
* It will also determine if the background color is light or dark to properly adjust the
|
||||
* appearance of the status bar so the font will not clash with the background.
|
||||
*
|
||||
* @param bgColor Background color
|
||||
*/
|
||||
private void updateStatusBar(int bgColor) {
|
||||
Window window = cordova.getActivity().getWindow();
|
||||
|
||||
View statusBar = getStatusBarView(webView);
|
||||
if (statusBar != null) {
|
||||
statusBar.setBackgroundColor(bgColor);
|
||||
}
|
||||
|
||||
// Automatically set the font and icon color of the system bars based on background color.
|
||||
boolean isStatusBarBackgroundColorLight;
|
||||
if(bgColor == Color.TRANSPARENT) {
|
||||
isStatusBarBackgroundColorLight = isColorLight(getUiModeColor());
|
||||
} else {
|
||||
isStatusBarBackgroundColorLight = isColorLight(bgColor);
|
||||
}
|
||||
WindowInsetsControllerCompat controllerCompat = WindowCompat.getInsetsController(window, window.getDecorView());
|
||||
controllerCompat.setAppearanceLightStatusBars(isStatusBarBackgroundColorLight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the supplied color's appearance is light.
|
||||
*
|
||||
* @param color color
|
||||
* @return boolean value true is returned when the color is light.
|
||||
*/
|
||||
private static boolean isColorLight(int color) {
|
||||
double r = Color.red(color) / 255.0;
|
||||
double g = Color.green(color) / 255.0;
|
||||
double b = Color.blue(color) / 255.0;
|
||||
double luminance = 0.299 * r + 0.587 * g + 0.114 * b;
|
||||
return luminance > 0.5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the StatusBarBackgroundColor preference value.
|
||||
* If the value is missing or fails to parse, it will attempt to try to guess the background
|
||||
* color by extracting from the apps R.color.cdv_background_color or determine from the uiModes.
|
||||
* If all fails, the color normally used in light mode is returned.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private int getPreferenceStatusBarBackgroundColor() {
|
||||
String colorString = preferences.getString("StatusBarBackgroundColor", null);
|
||||
|
||||
int parsedColor = parseColorFromString(colorString);
|
||||
if (parsedColor != INVALID_COLOR) return parsedColor;
|
||||
|
||||
return getUiModeColor(); // fallback
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the BackgroundColor preference value.
|
||||
* If missing or fails to decode, it will return INVALID_COLOR (-1).
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private int getPreferenceBackgroundColor() {
|
||||
try {
|
||||
return preferences.getInteger("BackgroundColor", INVALID_COLOR);
|
||||
} catch (NumberFormatException e) {
|
||||
LOG.e(PLUGIN_NAME, "Invalid background color argument. Example valid string: '0x00000000'");
|
||||
return INVALID_COLOR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find and return the rootLayout.
|
||||
*
|
||||
* @param webView CordovaWebView
|
||||
* @return FrameLayout|null
|
||||
*/
|
||||
private FrameLayout getRootLayout(CordovaWebView webView) {
|
||||
ViewParent parent = webView.getView().getParent();
|
||||
if (parent instanceof FrameLayout) {
|
||||
return (FrameLayout) parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find and return the statusBarView.
|
||||
*
|
||||
* @param webView CordovaWebView
|
||||
* @return View|null
|
||||
*/
|
||||
private View getStatusBarView(CordovaWebView webView) {
|
||||
FrameLayout rootView = getRootLayout(webView);
|
||||
if (rootView == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rootView.getChildCount(); i++) {
|
||||
View child = rootView.getChildAt(i);
|
||||
Object tag = child.getTag();
|
||||
if ("statusBarView".equals(tag)) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the background color for status bar & root layer.
|
||||
* The color will come from the app's R.color.cdv_background_color.
|
||||
* If for some reason the resource is missing, it will try to fallback on the uiMode.
|
||||
* <p>
|
||||
* The uiMode as follows.
|
||||
* If night mode: "#121318" (android.R.color.system_background_dark)
|
||||
* If day mode: "#FAF8FF" (android.R.color.system_background_light)
|
||||
* If all fails, light mode will be returned.
|
||||
* </p>
|
||||
* The hex values are supplied instead of "android.R.color" for backwards compatibility.
|
||||
*
|
||||
* @return int color
|
||||
*/
|
||||
@SuppressLint("DiscouragedApi")
|
||||
private int getUiModeColor() {
|
||||
boolean isNightMode = (resources.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
|
||||
String fallbackColor = isNightMode ? "#121318" : "#FAF8FF";
|
||||
int colorResId = resources.getIdentifier("cdv_background_color", "color", context.getPackageName());
|
||||
return colorResId != 0
|
||||
? ContextCompat.getColor(context, colorResId)
|
||||
: Color.parseColor(fallbackColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse color string that would be provided by app developers.
|
||||
* If the color string is empty or unable to parse, it will return INVALID_COLOR (-1).
|
||||
*
|
||||
* @param colorPref hex string value, #AARRGGBB or #RRGGBB
|
||||
* @return int
|
||||
*/
|
||||
private int parseColorFromString(final String colorPref) {
|
||||
if (colorPref.isEmpty()) return INVALID_COLOR;
|
||||
|
||||
try {
|
||||
return Color.parseColor(colorPref);
|
||||
} catch (IllegalArgumentException ignore) {
|
||||
LOG.e(PLUGIN_NAME, "Invalid color hex code. Valid format: #RRGGBB or #AARRGGBB");
|
||||
return INVALID_COLOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -114,8 +119,8 @@ public class SystemWebChromeClient extends WebChromeClient {
|
||||
* If the client returns true, WebView will assume that the client will
|
||||
* handle the prompt dialog and call the appropriate JsPromptResult method.
|
||||
*
|
||||
* Since we are hacking prompts for our own purposes, we should not be using them for
|
||||
* this purpose, perhaps we should hack console.log to do this instead!
|
||||
* <p>Since we are hacking prompts for our own purposes, we should not be using them for
|
||||
* this purpose, perhaps we should hack console.log to do this instead!</p>
|
||||
*/
|
||||
@Override
|
||||
public boolean onJsPrompt(WebView view, String origin, String message, String defaultValue, final JsPromptResult result) {
|
||||
@@ -150,15 +155,15 @@ public class SystemWebChromeClient extends WebChromeClient {
|
||||
quotaUpdater.updateQuota(MAX_QUOTA);
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Instructs the client to show a prompt to ask the user to set the Geolocation permission state for the specified origin.
|
||||
*
|
||||
* This also checks for the Geolocation Plugin and requests permission from the application to use Geolocation.
|
||||
* <p>This also checks for the Geolocation Plugin and requests permission from the application to use Geolocation.</p>
|
||||
*
|
||||
* @param origin
|
||||
* @param callback
|
||||
*/
|
||||
@Override
|
||||
public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) {
|
||||
super.onGeolocationPermissionsShowPrompt(origin, callback);
|
||||
callback.invoke(origin, true, false);
|
||||
@@ -183,12 +188,13 @@ public class SystemWebChromeClient extends WebChromeClient {
|
||||
parentEngine.getCordovaWebView().hideCustomView();
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Ask the host application for a custom progress view to show while
|
||||
* a <video> is loading.
|
||||
*
|
||||
* @return View The progress view.
|
||||
*/
|
||||
@Override
|
||||
public View getVideoLoadingProgressView() {
|
||||
if (mVideoProgressView == null) {
|
||||
// Create a new Loading view programmatically.
|
||||
@@ -199,7 +205,7 @@ public class SystemWebChromeClient extends WebChromeClient {
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
|
||||
layout.setLayoutParams(layoutParams);
|
||||
// the proress bar
|
||||
// the progress bar
|
||||
ProgressBar bar = new ProgressBar(parentEngine.getView().getContext());
|
||||
LinearLayout.LayoutParams barLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
barLayoutParams.gravity = Gravity.CENTER;
|
||||
@@ -212,52 +218,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()));
|
||||
|
||||
@@ -29,6 +29,8 @@ 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;
|
||||
@@ -116,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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,7 +199,7 @@ public class SystemWebViewClient extends WebViewClient {
|
||||
* one time for the main frame. This also means that onPageStarted will not be called when the contents of an
|
||||
* embedded frame changes, i.e. clicking a link whose target is an iframe.
|
||||
*
|
||||
* @param view The webview initiating the callback.
|
||||
* @param view The WebView initiating the callback.
|
||||
* @param url The url of the page.
|
||||
*/
|
||||
@Override
|
||||
@@ -202,7 +216,7 @@ public class SystemWebViewClient extends WebViewClient {
|
||||
* This method is called only for main frame. When onPageFinished() is called, the rendering picture may not be updated yet.
|
||||
*
|
||||
*
|
||||
* @param view The webview initiating the callback.
|
||||
* @param view The WebView initiating the callback.
|
||||
* @param url The url of the page.
|
||||
*/
|
||||
@Override
|
||||
@@ -214,7 +228,7 @@ public class SystemWebViewClient extends WebViewClient {
|
||||
}
|
||||
isCurrentlyLoading = false;
|
||||
|
||||
/**
|
||||
/*
|
||||
* Because of a timing issue we need to clear this history in onPageFinished as well as
|
||||
* onPageStarted. However we only want to do this if the doClearHistory boolean is set to
|
||||
* true. You see when you load a url with a # in it which is common in jQuery applications
|
||||
@@ -318,7 +332,6 @@ public class SystemWebViewClient extends WebViewClient {
|
||||
*
|
||||
* @param host
|
||||
* @param realm
|
||||
*
|
||||
* @return the authentication token or null if did not exist
|
||||
*/
|
||||
public AuthenticationToken removeAuthenticationToken(String host, String realm) {
|
||||
@@ -328,15 +341,16 @@ public class SystemWebViewClient extends WebViewClient {
|
||||
/**
|
||||
* Gets the authentication token.
|
||||
*
|
||||
* In order it tries:
|
||||
* 1- host + realm
|
||||
* 2- host
|
||||
* 3- realm
|
||||
* 4- no host, no realm
|
||||
* <p>In order it tries:</p>
|
||||
* <ol>
|
||||
* <li>host + realm</li>
|
||||
* <li>host</li>
|
||||
* <li>realm</li>
|
||||
* <li>no host, no realm</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param host
|
||||
* @param realm
|
||||
*
|
||||
* @return the authentication token
|
||||
*/
|
||||
public AuthenticationToken getAuthenticationToken(String host, String realm) {
|
||||
|
||||
@@ -110,7 +110,7 @@ public class SystemWebViewEngine implements CordovaWebViewEngine {
|
||||
nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode.OnlineEventsBridgeModeDelegate() {
|
||||
@Override
|
||||
public void setNetworkAvailable(boolean value) {
|
||||
//sometimes this can be called after calling webview.destroy() on destroy()
|
||||
//sometimes this can be called after calling webView.destroy() on destroy()
|
||||
//thus resulting in a NullPointerException
|
||||
if(webView!=null) {
|
||||
webView.setNetworkAvailable(value);
|
||||
@@ -175,9 +175,9 @@ public class SystemWebViewEngine implements CordovaWebViewEngine {
|
||||
String databasePath = webView.getContext().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
|
||||
settings.setDatabaseEnabled(true);
|
||||
|
||||
// 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.
|
||||
// The default is to use the module's debuggable state to decide if the WebView inspector
|
||||
// should be enabled. However, users can configure InspectableWebView preference to forcefully enable
|
||||
// or disable the WebView inspector.
|
||||
String inspectableWebview = preferences.getString("InspectableWebview", null);
|
||||
boolean shouldEnableInspector = false;
|
||||
if (inspectableWebview == null) {
|
||||
@@ -249,7 +249,7 @@ public class SystemWebViewEngine implements CordovaWebViewEngine {
|
||||
|
||||
|
||||
/**
|
||||
* Load the url into the webview.
|
||||
* Load the url into the WebView.
|
||||
*/
|
||||
@Override
|
||||
public void loadUrl(final String url, boolean clearNavigationStack) {
|
||||
@@ -288,7 +288,7 @@ public class SystemWebViewEngine implements CordovaWebViewEngine {
|
||||
*/
|
||||
@Override
|
||||
public boolean goBack() {
|
||||
// Check webview first to see if there is a history
|
||||
// Check WebView first to see if there is a history
|
||||
// This is needed to support curPage#diffLink, since they are added to parentEngine's history, but not our history url array (JQMobile behavior)
|
||||
if (webView.canGoBack()) {
|
||||
webView.goBack();
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const os = require('os');
|
||||
const os = require('node:os');
|
||||
const execa = require('execa');
|
||||
const events = require('cordova-common').events;
|
||||
const CordovaError = require('cordova-common').CordovaError;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const fs = require('node:fs');
|
||||
const xml = require('cordova-common').xmlHelpers;
|
||||
|
||||
const DEFAULT_ORIENTATION = 'default';
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const properties_parser = require('properties-parser');
|
||||
const pluginHandlers = require('./pluginHandlers');
|
||||
const CordovaGradleConfigParserFactory = require('./config/CordovaGradleConfigParserFactory');
|
||||
|
||||
12
lib/Api.js
12
lib/Api.js
@@ -17,7 +17,7 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const path = require('node:path');
|
||||
|
||||
const AndroidProject = require('./AndroidProject');
|
||||
const PluginManager = require('cordova-common').PluginManager;
|
||||
@@ -72,9 +72,9 @@ class Api {
|
||||
platformWww: path.join(this.root, 'platform_www'),
|
||||
configXml: path.join(appRes, 'xml', 'config.xml'),
|
||||
defaultConfigXml: path.join(this.root, 'cordova', 'defaults.xml'),
|
||||
strings: path.join(appRes, 'values', 'strings.xml'),
|
||||
themes: path.join(appRes, 'values', 'themes.xml'),
|
||||
colors: path.join(appRes, 'values', 'colors.xml'),
|
||||
strings: path.join(appRes, 'values', 'cdv_strings.xml'),
|
||||
themes: path.join(appRes, 'values', 'cdv_themes.xml'),
|
||||
colors: path.join(appRes, 'values', 'cdv_colors.xml'),
|
||||
manifest: path.join(appMain, 'AndroidManifest.xml'),
|
||||
build: path.join(this.root, 'build'),
|
||||
javaSrc: path.join(appMain, 'java')
|
||||
@@ -179,10 +179,6 @@ class Api {
|
||||
removePlugin (plugin, uninstallOptions) {
|
||||
const project = AndroidProject.getProjectFile(this.root);
|
||||
|
||||
if (uninstallOptions && uninstallOptions.usePlatformWww === true) {
|
||||
uninstallOptions.usePlatformWww = false;
|
||||
}
|
||||
|
||||
return PluginManager.get(this.platform, this.locations, project)
|
||||
.removePlugin(plugin, uninstallOptions)
|
||||
.then(function () {
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const path = require('node:path');
|
||||
const fs = require('node:fs');
|
||||
const nopt = require('nopt');
|
||||
const untildify = require('untildify');
|
||||
const { parseArgsStringToArgv } = require('string-argv');
|
||||
|
||||
@@ -17,15 +17,16 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const fs = require('node:fs');
|
||||
const fsp = require('node:fs/promises');
|
||||
const path = require('node:path');
|
||||
const execa = require('execa');
|
||||
const glob = require('fast-glob');
|
||||
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');
|
||||
|
||||
@@ -85,9 +86,7 @@ class ProjectBuilder {
|
||||
}
|
||||
|
||||
getArgs (cmd, opts) {
|
||||
let args = [
|
||||
'-b', path.join(this.root, 'build.gradle')
|
||||
];
|
||||
let args = [];
|
||||
if (opts.extraArgs) {
|
||||
args = args.concat(opts.extraArgs);
|
||||
}
|
||||
@@ -116,16 +115,27 @@ class ProjectBuilder {
|
||||
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' });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,9 +173,9 @@ class ProjectBuilder {
|
||||
// This is the future-proof way of checking if a file exists
|
||||
// This must be synchronous to satisfy a Travis test
|
||||
try {
|
||||
fs.accessSync(subProjectGradle, fs.F_OK);
|
||||
fs.accessSync(subProjectGradle, fs.constants.F_OK);
|
||||
} catch (e) {
|
||||
fs.copySync(pluginBuildGradle, subProjectGradle);
|
||||
fs.cpSync(pluginBuildGradle, subProjectGradle);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -195,10 +205,11 @@ class ProjectBuilder {
|
||||
'// GENERATED FILE - DO NOT EDIT\n' +
|
||||
'apply from: "cdv-gradle-name.gradle"\n' +
|
||||
'include ":"\n' +
|
||||
settingsGradlePaths.join(''));
|
||||
settingsGradlePaths.join('') +
|
||||
'\nif (file("settings-extras.gradle").exists()) {\n apply from: "settings-extras.gradle"\n}\n');
|
||||
|
||||
// Touch empty cdv-gradle-name.gradle file if missing.
|
||||
if (!fs.pathExistsSync(path.join(this.root, 'cdv-gradle-name.gradle'))) {
|
||||
if (!fs.existsSync(path.join(this.root, 'cdv-gradle-name.gradle'))) {
|
||||
fs.writeFileSync(path.join(this.root, 'cdv-gradle-name.gradle'), '');
|
||||
}
|
||||
|
||||
@@ -275,31 +286,61 @@ class ProjectBuilder {
|
||||
|
||||
prepEnv (opts) {
|
||||
const self = this;
|
||||
const config = this._getCordovaConfig();
|
||||
const configPropertiesPath = path.join(self.root, 'tools', '.gradle', 'config.properties');
|
||||
|
||||
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(async function () {
|
||||
try {
|
||||
// Try to create "config.properties" file if missing
|
||||
const fd = fs.openSync(configPropertiesPath, 'wx+', 0o600);
|
||||
fs.writeFileSync(fd, '', 'utf8');
|
||||
fs.closeSync(fd);
|
||||
} catch {
|
||||
// File already existed, nothing to do.
|
||||
}
|
||||
|
||||
// Loads "config.properties"for editing
|
||||
const configProperties = createEditor(configPropertiesPath);
|
||||
/*
|
||||
* File is replaced on each build. "before_build" hook scripts can inject
|
||||
* custom settings before this step runs. This step will still overwrite
|
||||
* the "java.home" setting. Ensure environment variables are properly
|
||||
* configured if want to use custom "java.home".
|
||||
*
|
||||
* Sets "java.home" using the "CORDOVA_JAVA_HOME" environment variable.
|
||||
* If unavailable, fallback to "JAVA_HOME".
|
||||
* If neither is set, "java.home" is removed (if previously set),
|
||||
* allowing Android Studio to display a warning and auto-configure
|
||||
* to use its internal (bundled) Java.
|
||||
*/
|
||||
const javaHome = process.env.CORDOVA_JAVA_HOME || process.env.JAVA_HOME || false;
|
||||
if (javaHome) {
|
||||
configProperties.set('java.home', javaHome);
|
||||
} else {
|
||||
configProperties.unset('java.home');
|
||||
}
|
||||
// Saves changes
|
||||
configProperties.save();
|
||||
}).then(async function () {
|
||||
await fsp.cp(path.join(self.root, 'tools', 'gradle'), path.join(self.root, 'gradle'), { recursive: true, force: true });
|
||||
await fsp.cp(path.join(self.root, 'tools', 'gradlew'), path.join(self.root, 'gradlew'), { recursive: true, force: true });
|
||||
await fsp.cp(path.join(self.root, 'tools', 'gradlew.bat'), path.join(self.root, 'gradlew.bat'), { recursive: true, force: true });
|
||||
await fsp.cp(path.join(self.root, 'tools', '.gradle'), path.join(self.root, '.gradle'), { recursive: true, force: true });
|
||||
}).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);
|
||||
if (opts.packageInfo) {
|
||||
fs.ensureFileSync(signingPropertiesPath);
|
||||
fs.writeFileSync(signingPropertiesPath, '', 'utf8');
|
||||
const signingProperties = createEditor(signingPropertiesPath);
|
||||
signingProperties.addHeadComment(TEMPLATE);
|
||||
opts.packageInfo.appendToProperties(signingProperties);
|
||||
} else {
|
||||
fs.rmSync(signingPropertiesPath, { force: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -309,7 +350,7 @@ class ProjectBuilder {
|
||||
* @returns The user defined configs
|
||||
*/
|
||||
_getCordovaConfig () {
|
||||
return fs.readJSONSync(path.join(this.root, 'cdv-gradle-config.json'));
|
||||
return JSON.parse(fs.readFileSync(path.join(this.root, 'cdv-gradle-config.json'), 'utf-8') || '{}');
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -317,7 +358,7 @@ 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(' ')}`);
|
||||
@@ -338,11 +379,11 @@ 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(() => {
|
||||
fs.removeSync(path.join(this.root, 'out'));
|
||||
fs.rmSync(path.join(this.root, 'out'), { recursive: true, force: true });
|
||||
|
||||
['debug', 'release'].map(config => path.join(this.root, `${config}${SIGNING_PROPERTIES}`))
|
||||
.forEach(file => {
|
||||
@@ -350,7 +391,7 @@ class ProjectBuilder {
|
||||
const hasMarker = hasFile && fs.readFileSync(file, 'utf8')
|
||||
.includes(MARKER);
|
||||
|
||||
if (hasFile && hasMarker) fs.removeSync(file);
|
||||
if (hasFile && hasMarker) fs.rmSync(file);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -17,9 +17,8 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const execa = require('execa');
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('node:path');
|
||||
const fs = require('node:fs');
|
||||
const { forgivingWhichSync, isWindows, isDarwin } = require('./utils');
|
||||
const java = require('./env/java');
|
||||
const { CordovaError, ConfigParser, events } = require('cordova-common');
|
||||
@@ -104,44 +103,7 @@ function getUserCompileSdkVersion (projectRoot) {
|
||||
}
|
||||
|
||||
module.exports.get_gradle_wrapper = function () {
|
||||
let androidStudioPath;
|
||||
let i = 0;
|
||||
let foundStudio = false;
|
||||
let program_dir;
|
||||
// OK, This hack only works on Windows, not on Mac OS or Linux. We will be deleting this eventually!
|
||||
if (module.exports.isWindows()) {
|
||||
const result = execa.sync(path.join(__dirname, 'getASPath.bat'));
|
||||
// console.log('result.stdout =' + result.stdout.toString());
|
||||
// console.log('result.stderr =' + result.stderr.toString());
|
||||
|
||||
if (result.stderr.toString().length > 0) {
|
||||
const androidPath = path.join(process.env.ProgramFiles, 'Android') + '/';
|
||||
if (fs.existsSync(androidPath)) {
|
||||
program_dir = fs.readdirSync(androidPath);
|
||||
while (i < program_dir.length && !foundStudio) {
|
||||
if (program_dir[i].startsWith('Android Studio')) {
|
||||
foundStudio = true;
|
||||
androidStudioPath = path.join(process.env.ProgramFiles, 'Android', program_dir[i], 'gradle');
|
||||
} else { ++i; }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// console.log('got android studio path from registry');
|
||||
// remove the (os independent) new line char at the end of stdout
|
||||
// add gradle to match the above.
|
||||
androidStudioPath = path.join(result.stdout.toString().split('\r\n')[0], 'gradle');
|
||||
}
|
||||
}
|
||||
|
||||
if (androidStudioPath !== null && fs.existsSync(androidStudioPath)) {
|
||||
const dirs = fs.readdirSync(androidStudioPath);
|
||||
if (dirs[0].split('-')[0] === 'gradle') {
|
||||
return path.join(androidStudioPath, dirs[0], 'bin', 'gradle');
|
||||
}
|
||||
} else {
|
||||
// OK, let's try to check for Gradle!
|
||||
return forgivingWhichSync('gradle');
|
||||
}
|
||||
return forgivingWhichSync('gradle');
|
||||
};
|
||||
|
||||
// Returns a promise. Called only by build and clean commands.
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const events = require('cordova-common').events;
|
||||
|
||||
class CordovaGradleConfigParser {
|
||||
@@ -41,7 +41,7 @@ class CordovaGradleConfigParser {
|
||||
* @returns {Record<any, any>} The parsed JSON object representing the gradle config.
|
||||
*/
|
||||
_readConfig (configPath) {
|
||||
return fs.readJSONSync(configPath, 'utf-8');
|
||||
return JSON.parse(fs.readFileSync(configPath, 'utf-8') || '{}');
|
||||
}
|
||||
|
||||
setPackageName (packageName) {
|
||||
@@ -64,7 +64,7 @@ class CordovaGradleConfigParser {
|
||||
*/
|
||||
write () {
|
||||
events.emit('verbose', '[Cordova Gradle Config] Saving File');
|
||||
fs.writeJSONSync(this._cdvGradleConfigFilePath, this._cdvGradleConfig, 'utf-8');
|
||||
fs.writeFileSync(this._cdvGradleConfigFilePath, JSON.stringify(this._cdvGradleConfig, null, 2), 'utf-8');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const propertiesParser = require('properties-parser');
|
||||
const events = require('cordova-common').events;
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('node:path');
|
||||
const fs = require('node:fs');
|
||||
const utils = require('./utils');
|
||||
const check_reqs = require('./check_reqs');
|
||||
const ROOT = path.join(__dirname, '..');
|
||||
@@ -49,27 +49,27 @@ function copyJsAndLibrary (projectPath, shared, projectName, targetAPI) {
|
||||
const app_path = path.join(projectPath, 'app', 'src', 'main');
|
||||
const platform_www = path.join(projectPath, 'platform_www');
|
||||
|
||||
fs.copySync(srcCordovaJsPath, path.join(app_path, 'assets', 'www', 'cordova.js'));
|
||||
fs.cpSync(srcCordovaJsPath, path.join(app_path, 'assets', 'www', 'cordova.js'));
|
||||
|
||||
// Copy the cordova.js file to platforms/<platform>/platform_www/
|
||||
// The www dir is nuked on each prepare so we keep cordova.js in platform_www
|
||||
fs.ensureDirSync(platform_www);
|
||||
fs.copySync(srcCordovaJsPath, path.join(platform_www, 'cordova.js'));
|
||||
fs.mkdirSync(platform_www, { recursive: true });
|
||||
fs.cpSync(srcCordovaJsPath, path.join(platform_www, 'cordova.js'));
|
||||
fs.cpSync(path.join(ROOT, 'framework', 'cdv-gradle-config-defaults.json'), path.join(projectPath, 'cdv-gradle-config.json'));
|
||||
|
||||
if (shared) {
|
||||
const relativeFrameworkPath = path.relative(projectPath, getFrameworkDir(projectPath, true));
|
||||
fs.symlinkSync(relativeFrameworkPath, nestedCordovaLibPath, 'dir');
|
||||
} else {
|
||||
fs.ensureDirSync(nestedCordovaLibPath);
|
||||
fs.copySync(path.join(ROOT, 'framework', 'AndroidManifest.xml'), path.join(nestedCordovaLibPath, 'AndroidManifest.xml'));
|
||||
fs.mkdirSync(nestedCordovaLibPath, { recursive: true });
|
||||
fs.cpSync(path.join(ROOT, 'framework', 'AndroidManifest.xml'), path.join(nestedCordovaLibPath, 'AndroidManifest.xml'));
|
||||
const propertiesEditor = createEditor(path.join(ROOT, 'framework', 'project.properties'));
|
||||
propertiesEditor.set('target', targetAPI);
|
||||
propertiesEditor.save(path.join(nestedCordovaLibPath, 'project.properties'));
|
||||
fs.copySync(path.join(ROOT, 'framework', 'build.gradle'), path.join(nestedCordovaLibPath, 'build.gradle'));
|
||||
fs.copySync(path.join(ROOT, 'framework', 'cordova.gradle'), path.join(nestedCordovaLibPath, 'cordova.gradle'));
|
||||
fs.copySync(path.join(ROOT, 'framework', 'repositories.gradle'), path.join(nestedCordovaLibPath, 'repositories.gradle'));
|
||||
fs.copySync(path.join(ROOT, 'framework', 'src'), path.join(nestedCordovaLibPath, 'src'));
|
||||
fs.copySync(path.join(ROOT, 'framework', 'cdv-gradle-config-defaults.json'), path.join(projectPath, 'cdv-gradle-config.json'));
|
||||
fs.cpSync(path.join(ROOT, 'framework', 'build.gradle'), path.join(nestedCordovaLibPath, 'build.gradle'));
|
||||
fs.cpSync(path.join(ROOT, 'framework', 'cordova.gradle'), path.join(nestedCordovaLibPath, 'cordova.gradle'));
|
||||
fs.cpSync(path.join(ROOT, 'framework', 'repositories.gradle'), path.join(nestedCordovaLibPath, 'repositories.gradle'));
|
||||
fs.cpSync(path.join(ROOT, 'framework', 'src'), path.join(nestedCordovaLibPath, 'src'), { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,29 +113,24 @@ 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.cpSync(path.join(srcDir, 'build.gradle'), path.join(projectPath, 'build.gradle'));
|
||||
fs.cpSync(path.join(srcDir, 'app', 'build.gradle'), path.join(projectPath, 'app', 'build.gradle'));
|
||||
fs.cpSync(path.join(srcDir, 'app', 'repositories.gradle'), path.join(projectPath, 'app', 'repositories.gradle'));
|
||||
fs.cpSync(path.join(srcDir, 'repositories.gradle'), path.join(projectPath, 'repositories.gradle'));
|
||||
|
||||
copyGradleTools(projectPath);
|
||||
}
|
||||
|
||||
function copyScripts (projectPath) {
|
||||
const srcScriptsDir = path.join(ROOT, 'templates', 'cordova');
|
||||
const destScriptsDir = path.join(projectPath, 'cordova');
|
||||
// Delete old scripts directory if this is an update.
|
||||
fs.removeSync(destScriptsDir);
|
||||
fs.rmSync(destScriptsDir, { recursive: true, force: true });
|
||||
// Copy in the new ones.
|
||||
fs.copySync(srcScriptsDir, destScriptsDir);
|
||||
fs.cpSync(srcScriptsDir, destScriptsDir, { recursive: true });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,6 +171,12 @@ function validateProjectName (project_name) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function copyGradleTools (projectPath) {
|
||||
const srcDir = path.join(ROOT, 'templates', 'project');
|
||||
|
||||
fs.cpSync(path.resolve(srcDir, 'tools'), path.resolve(projectPath, 'tools'), { recursive: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an android application with the given options.
|
||||
*
|
||||
@@ -204,7 +205,7 @@ exports.create = function (project_path, config, options, events) {
|
||||
return Promise.reject(new CordovaError('Project already exists! Delete and recreate'));
|
||||
}
|
||||
|
||||
const package_name = config.android_packageName() || config.packageName() || 'io.cordova.helloCordova';
|
||||
const package_name = config.android_packageName() || config.packageName() || 'org.apache.cordova.hellocordova';
|
||||
const project_name = config.name() || 'Hello Cordova';
|
||||
|
||||
const safe_activity_name = config.android_activityName() || options.activityName || 'MainActivity';
|
||||
@@ -231,13 +232,13 @@ exports.create = function (project_path, config, options, events) {
|
||||
const app_path = path.join(project_path, 'app', 'src', 'main');
|
||||
|
||||
// copy project template
|
||||
fs.ensureDirSync(app_path);
|
||||
fs.copySync(path.join(project_template_dir, 'assets'), path.join(app_path, 'assets'));
|
||||
fs.copySync(path.join(project_template_dir, 'res'), path.join(app_path, 'res'));
|
||||
fs.copySync(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore'));
|
||||
fs.mkdirSync(app_path, { recursive: true });
|
||||
fs.cpSync(path.join(project_template_dir, 'assets'), path.join(app_path, 'assets'), { recursive: true });
|
||||
fs.cpSync(path.join(project_template_dir, 'res'), path.join(app_path, 'res'), { recursive: true });
|
||||
fs.cpSync(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore'));
|
||||
|
||||
// Manually create directories that would be empty within the template (since git doesn't track directories).
|
||||
fs.ensureDirSync(path.join(app_path, 'libs'));
|
||||
fs.mkdirSync(path.join(app_path, 'libs'), { recursive: true });
|
||||
|
||||
// copy cordova.js, cordova.jar
|
||||
exports.copyJsAndLibrary(project_path, options.link, safe_activity_name, target_api);
|
||||
@@ -246,9 +247,9 @@ exports.create = function (project_path, config, options, events) {
|
||||
const java_path = path.join(app_path, 'java');
|
||||
const assets_path = path.join(app_path, 'assets');
|
||||
const resource_path = path.join(app_path, 'res');
|
||||
fs.ensureDirSync(java_path);
|
||||
fs.ensureDirSync(assets_path);
|
||||
fs.ensureDirSync(resource_path);
|
||||
fs.mkdirSync(java_path, { recursive: true });
|
||||
fs.mkdirSync(assets_path, { recursive: true });
|
||||
fs.mkdirSync(resource_path, { recursive: true });
|
||||
|
||||
// store package name in cdv-gradle-config
|
||||
const cdvGradleConfig = CordovaGradleConfigParserFactory.create(project_path);
|
||||
@@ -260,10 +261,10 @@ exports.create = function (project_path, config, options, events) {
|
||||
const activity_dir = path.join(java_path, packagePath);
|
||||
const activity_path = path.join(activity_dir, safe_activity_name + '.java');
|
||||
|
||||
fs.ensureDirSync(activity_dir);
|
||||
fs.copySync(path.join(project_template_dir, 'Activity.java'), activity_path);
|
||||
fs.mkdirSync(activity_dir, { recursive: true });
|
||||
fs.cpSync(path.join(project_template_dir, 'Activity.java'), activity_path);
|
||||
utils.replaceFileContents(activity_path, /__ACTIVITY__/, safe_activity_name);
|
||||
utils.replaceFileContents(path.join(app_path, 'res', 'values', 'strings.xml'), /__NAME__/, utils.escape(project_name));
|
||||
utils.replaceFileContents(path.join(app_path, 'res', 'values', 'cdv_strings.xml'), /__NAME__/, utils.escape(project_name));
|
||||
utils.replaceFileContents(activity_path, /__ID__/, package_name);
|
||||
|
||||
const manifest = new AndroidManifest(path.join(project_template_dir, 'AndroidManifest.xml'));
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
*/
|
||||
|
||||
const execa = require('execa');
|
||||
const fs = require('fs-extra');
|
||||
const fs = require('node:fs');
|
||||
const android_versions = require('android-versions');
|
||||
const path = require('path');
|
||||
const path = require('node:path');
|
||||
const Adb = require('./Adb');
|
||||
const events = require('cordova-common').events;
|
||||
const CordovaError = require('cordova-common').CordovaError;
|
||||
|
||||
4
lib/env/java.js
vendored
4
lib/env/java.js
vendored
@@ -18,8 +18,8 @@
|
||||
*/
|
||||
|
||||
const execa = require('execa');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const glob = require('fast-glob');
|
||||
const { CordovaError, events } = require('cordova-common');
|
||||
const utils = require('../utils');
|
||||
|
||||
@@ -1,20 +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.
|
||||
|
||||
@ECHO OFF
|
||||
for /f "tokens=2*" %%a in ('REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Android Studio" /v Path') do set "ASPath=%%~b"
|
||||
ECHO %ASPath%
|
||||
@@ -14,8 +14,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const isPathInside = require('is-path-inside');
|
||||
const events = require('cordova-common').events;
|
||||
const CordovaError = require('cordova-common').CordovaError;
|
||||
@@ -42,7 +42,7 @@ const handlers = {
|
||||
deleteJava(project.projectDir, dest);
|
||||
} else {
|
||||
// Just remove the file, not the whole parent directory
|
||||
removeFile(path.resolve(project.projectDir, dest));
|
||||
removeFileF(path.resolve(project.projectDir, dest));
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -53,7 +53,7 @@ const handlers = {
|
||||
},
|
||||
uninstall: function (obj, plugin, project, options) {
|
||||
const dest = path.join('app/libs', path.basename(obj.src));
|
||||
removeFile(path.resolve(project.projectDir, dest));
|
||||
removeFileF(path.resolve(project.projectDir, dest));
|
||||
}
|
||||
},
|
||||
'resource-file': {
|
||||
@@ -63,7 +63,7 @@ const handlers = {
|
||||
},
|
||||
uninstall: function (obj, plugin, project, options) {
|
||||
const dest = path.join('app', 'src', 'main', obj.target);
|
||||
removeFile(path.resolve(project.projectDir, dest));
|
||||
removeFileF(path.resolve(project.projectDir, dest));
|
||||
}
|
||||
},
|
||||
framework: {
|
||||
@@ -102,7 +102,7 @@ const handlers = {
|
||||
|
||||
if (obj.custom) {
|
||||
const subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
|
||||
removeFile(path.resolve(project.projectDir, subRelativeDir));
|
||||
removeFileF(path.resolve(project.projectDir, subRelativeDir));
|
||||
subDir = path.resolve(project.projectDir, subRelativeDir);
|
||||
// If it's the last framework in the plugin, remove the parent directory.
|
||||
const parDir = path.dirname(subDir);
|
||||
@@ -141,14 +141,13 @@ const handlers = {
|
||||
uninstall: function (obj, plugin, project, options) {
|
||||
const target = obj.target || obj.src;
|
||||
|
||||
if (!target) throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
|
||||
if (!target) {
|
||||
throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
|
||||
}
|
||||
|
||||
removeFile(path.resolve(project.www, target));
|
||||
removeFile(path.resolve(project.www, 'plugins', plugin.id));
|
||||
removeFileAndParents(project.www, target);
|
||||
if (options && options.usePlatformWww) {
|
||||
// CB-11022 remove file from both directories if usePlatformWww is specified
|
||||
removeFile(path.resolve(project.platformWww, target));
|
||||
removeFile(path.resolve(project.platformWww, 'plugins', plugin.id));
|
||||
removeFileAndParents(project.platformWww, target);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -166,13 +165,13 @@ const handlers = {
|
||||
scriptContent = 'cordova.define("' + moduleName + '", function(require, exports, module) {\n' + scriptContent + '\n});\n';
|
||||
|
||||
const wwwDest = path.resolve(project.www, 'plugins', plugin.id, obj.src);
|
||||
fs.ensureDirSync(path.dirname(wwwDest));
|
||||
fs.mkdirSync(path.dirname(wwwDest), { recursive: true });
|
||||
fs.writeFileSync(wwwDest, scriptContent, 'utf-8');
|
||||
|
||||
if (options && options.usePlatformWww) {
|
||||
// CB-11022 copy file to both directories if usePlatformWww is specified
|
||||
const platformWwwDest = path.resolve(project.platformWww, 'plugins', plugin.id, obj.src);
|
||||
fs.ensureDirSync(path.dirname(platformWwwDest));
|
||||
fs.mkdirSync(path.dirname(platformWwwDest), { recursive: true });
|
||||
fs.writeFileSync(platformWwwDest, scriptContent, 'utf-8');
|
||||
}
|
||||
},
|
||||
@@ -217,11 +216,11 @@ function copyFile (plugin_dir, src, project_dir, dest, link) {
|
||||
// check that dest path is located in project directory
|
||||
if (!isPathInside(dest, project_dir)) { throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project'); }
|
||||
|
||||
fs.ensureDirSync(path.dirname(dest));
|
||||
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
||||
if (link) {
|
||||
symlinkFileOrDirTree(src, dest);
|
||||
} else {
|
||||
fs.copySync(src, dest);
|
||||
fs.cpSync(src, dest, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,11 +234,11 @@ function copyNewFile (plugin_dir, src, project_dir, dest, link) {
|
||||
|
||||
function symlinkFileOrDirTree (src, dest) {
|
||||
if (fs.existsSync(dest)) {
|
||||
fs.removeSync(dest);
|
||||
fs.rmSync(dest, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
if (fs.statSync(src).isDirectory()) {
|
||||
fs.ensureDirSync(path.dirname(dest));
|
||||
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
||||
fs.readdirSync(src).forEach(function (entry) {
|
||||
symlinkFileOrDirTree(path.join(src, entry), path.join(dest, entry));
|
||||
});
|
||||
@@ -248,8 +247,8 @@ function symlinkFileOrDirTree (src, dest) {
|
||||
}
|
||||
}
|
||||
|
||||
function removeFile (file) {
|
||||
fs.removeSync(file);
|
||||
function removeFileF (file) {
|
||||
fs.rmSync(file, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
// Sometimes we want to remove some java, and prune any unnecessary empty directories
|
||||
@@ -262,7 +261,7 @@ function removeFileAndParents (baseDir, destFile, stopper) {
|
||||
const file = path.resolve(baseDir, destFile);
|
||||
if (!fs.existsSync(file)) return;
|
||||
|
||||
removeFile(file);
|
||||
removeFileF(file);
|
||||
|
||||
// check if directory is empty
|
||||
let curDir = path.dirname(file);
|
||||
|
||||
141
lib/prepare.js
141
lib/prepare.js
@@ -17,10 +17,11 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node: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;
|
||||
@@ -92,7 +93,7 @@ function updateUserProjectGradleConfig (project) {
|
||||
|
||||
// Write out changes
|
||||
const projectGradleConfigPath = path.join(project.root, 'cdv-gradle-config.json');
|
||||
fs.writeJSONSync(projectGradleConfigPath, projectGradleConfig, { spaces: 2 });
|
||||
fs.writeFileSync(projectGradleConfigPath, JSON.stringify(projectGradleConfig, null, 2), 'utf-8');
|
||||
}
|
||||
|
||||
function getUserGradleConfig (configXml) {
|
||||
@@ -109,7 +110,11 @@ 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 },
|
||||
{ xmlKey: 'AndroidShowDeprecations', gradleKey: 'JAVA_SHOW_DEPRECATIONS', type: Boolean }
|
||||
];
|
||||
|
||||
return configXmlToGradleMapping.reduce((config, mapping) => {
|
||||
@@ -194,7 +199,7 @@ function updateConfigFilesFrom (sourceConfig, configMunger, locations) {
|
||||
|
||||
// First cleanup current config and merge project's one into own
|
||||
// Overwrite platform config.xml with defaults.xml.
|
||||
fs.copySync(locations.defaultConfigXml, locations.configXml);
|
||||
fs.cpSync(locations.defaultConfigXml, locations.configXml);
|
||||
|
||||
// Then apply config changes from global munge to all config files
|
||||
// in project (including project's config)
|
||||
@@ -267,7 +272,7 @@ function cleanWww (projectRoot, locations) {
|
||||
*/
|
||||
function updateProjectAccordingTo (platformConfig, locations) {
|
||||
updateProjectStrings(platformConfig, locations);
|
||||
updateProjectSplashScreen(platformConfig, locations);
|
||||
updateProjectTheme(platformConfig, locations);
|
||||
|
||||
const name = platformConfig.name();
|
||||
|
||||
@@ -312,14 +317,14 @@ function updateProjectAccordingTo (platformConfig, locations) {
|
||||
const newDestFile = path.join(locations.root, 'app', 'src', 'main', 'java', androidPkgName.replace(/\./g, '/'), path.basename(destFile));
|
||||
if (newDestFile.toLowerCase() !== destFile.toLowerCase()) {
|
||||
// If package was name changed we need to create new java with main activity in path matching new package name
|
||||
fs.ensureDirSync(path.dirname(newDestFile));
|
||||
fs.mkdirSync(path.dirname(newDestFile), { recursive: true });
|
||||
events.emit('verbose', `copy ${destFile} to ${newDestFile}`);
|
||||
fs.copySync(destFile, newDestFile);
|
||||
fs.cpSync(destFile, newDestFile);
|
||||
utils.replaceFileContents(newDestFile, /package [\w.]*;/, 'package ' + androidPkgName + ';');
|
||||
events.emit('verbose', 'Wrote out Android package name "' + androidPkgName + '" to ' + newDestFile);
|
||||
// If package was name changed we need to remove old java with main activity
|
||||
events.emit('verbose', `remove ${destFile}`);
|
||||
fs.removeSync(destFile);
|
||||
fs.rmSync(destFile);
|
||||
// remove any empty directories
|
||||
let currentDir = path.dirname(destFile);
|
||||
const sourcesRoot = path.resolve(locations.root, 'src');
|
||||
@@ -342,7 +347,7 @@ function updateProjectAccordingTo (platformConfig, locations) {
|
||||
* @param {Object} locations A map of locations for this platform
|
||||
*/
|
||||
function updateProjectStrings (platformConfig, locations) {
|
||||
// Update app name by editing res/values/strings.xml
|
||||
// Update app name by editing res/values/cdv_strings.xml
|
||||
const strings = xmlHelpers.parseElementtreeSync(locations.strings);
|
||||
|
||||
const name = platformConfig.name();
|
||||
@@ -372,15 +377,34 @@ function warnForDeprecatedSplashScreen (cordovaProject) {
|
||||
* be used to update project
|
||||
* @param {Object} locations A map of locations for this platform
|
||||
*/
|
||||
function updateProjectSplashScreen (platformConfig, locations) {
|
||||
// res/values/themes.xml
|
||||
function updateProjectTheme (platformConfig, locations) {
|
||||
// res/values/cdv_themes.xml
|
||||
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);
|
||||
}
|
||||
if (!splashBg) {
|
||||
splashBg = '@color/cdv_splashscreen_background';
|
||||
}
|
||||
|
||||
events.emit('verbose', 'The Android Splash Screen background color was set to: ' +
|
||||
(splashBg === '@color/cdv_splashscreen_background' ? 'Default' : splashBg)
|
||||
);
|
||||
|
||||
// force the themes value to `@color/cdv_splashscreen_background`
|
||||
const splashBgNode = splashScreenTheme.find('item[@name="windowSplashScreenBackground"]');
|
||||
splashBgNode.text = splashBg;
|
||||
|
||||
[
|
||||
// Splash Screen
|
||||
'windowSplashScreenAnimatedIcon',
|
||||
'windowSplashScreenAnimationDuration',
|
||||
'windowSplashScreenBackground',
|
||||
'android:windowSplashScreenBrandingImage',
|
||||
'windowSplashScreenIconBackgroundColor',
|
||||
'postSplashScreenTheme'
|
||||
@@ -391,14 +415,6 @@ function updateProjectSplashScreen (platformConfig, locations) {
|
||||
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":
|
||||
@@ -440,7 +456,7 @@ function updateProjectSplashScreen (platformConfig, locations) {
|
||||
break;
|
||||
|
||||
case 'windowSplashScreenIconBackgroundColor':
|
||||
// use the user defined value for "colors.xml"
|
||||
// use the user defined value for "cdv_colors.xml"
|
||||
updateProjectSplashScreenIconBackgroundColor(cdvConfigPrefValue, locations);
|
||||
|
||||
// force the themes value to `@color/cdv_splashscreen_icon_background`
|
||||
@@ -464,7 +480,7 @@ function updateProjectSplashScreen (platformConfig, locations) {
|
||||
break;
|
||||
|
||||
case 'postSplashScreenTheme':
|
||||
themeTargetNode.text = cdvConfigPrefValue || '@style/Theme.AppCompat.NoActionBar';
|
||||
themeTargetNode.text = cdvConfigPrefValue || '@style/Theme.Cordova.App.DayNight';
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -476,29 +492,13 @@ function updateProjectSplashScreen (platformConfig, locations) {
|
||||
events.emit('verbose', 'Wrote out Android application themes to ' + locations.themes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} splashBackgroundColor SplashScreen Background Color Hex Code
|
||||
* be used to update project
|
||||
* @param {Object} locations A map of locations for this platform
|
||||
*/
|
||||
function updateProjectSplashScreenBackgroundColor (splashBackgroundColor, locations) {
|
||||
if (!splashBackgroundColor) { splashBackgroundColor = '#FFFFFF'; }
|
||||
|
||||
// res/values/colors.xml
|
||||
const colors = xmlHelpers.parseElementtreeSync(locations.colors);
|
||||
colors.find('color[@name="cdv_splashscreen_background"]').text = splashBackgroundColor.replace(/'/g, '\\\'');
|
||||
|
||||
fs.writeFileSync(locations.colors, colors.write({ indent: 4 }), 'utf-8');
|
||||
events.emit('verbose', 'Wrote out Android application SplashScreen Color to ' + locations.colors);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} splashIconBackgroundColor SplashScreen Icon Background Color Hex Code
|
||||
* be used to update project
|
||||
* @param {Object} locations A map of locations for this platform
|
||||
*/
|
||||
function updateProjectSplashScreenIconBackgroundColor (splashIconBackgroundColor, locations) {
|
||||
// res/values/colors.xml
|
||||
// res/values/cdv_colors.xml
|
||||
const colors = xmlHelpers.parseElementtreeSync(locations.colors);
|
||||
// node name
|
||||
const name = 'cdv_splashscreen_icon_background';
|
||||
@@ -526,16 +526,16 @@ function updateProjectSplashScreenIconBackgroundColor (splashIconBackgroundColor
|
||||
|
||||
function cleanupAndSetProjectSplashScreenImage (srcFile, destFilePath, possiblePreviousDestFilePath, cleanupOnly = false) {
|
||||
if (fs.existsSync(possiblePreviousDestFilePath)) {
|
||||
fs.removeSync(possiblePreviousDestFilePath);
|
||||
fs.rmSync(possiblePreviousDestFilePath);
|
||||
}
|
||||
|
||||
if (cleanupOnly && fs.existsSync(destFilePath)) {
|
||||
// Also remove dest file path for cleanup even if previous was not use.
|
||||
fs.removeSync(destFilePath);
|
||||
fs.rmSync(destFilePath);
|
||||
}
|
||||
|
||||
if (!cleanupOnly && srcFile && fs.existsSync(srcFile)) {
|
||||
fs.copySync(srcFile, destFilePath);
|
||||
fs.cpSync(srcFile, destFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -596,12 +596,12 @@ function updateProjectSplashScreenImage (locations, themeKey, cdvConfigPrefKey,
|
||||
|
||||
// copy the png to correct mipmap folder with name of ic_cdv_splashscreen.png
|
||||
// delete ic_cdv_splashscreen.xml from drawable folder
|
||||
// update themes.xml windowSplashScreenAnimatedIcon value to @mipmap/ic_cdv_splashscreen
|
||||
// update cdv_themes.xml windowSplashScreenAnimatedIcon value to @mipmap/ic_cdv_splashscreen
|
||||
cleanupAndSetProjectSplashScreenImage(cdvConfigPrefValue, destFilePath, possiblePreviousDestFilePath);
|
||||
} else if (iconExtension === '.xml') {
|
||||
// copy the xml to drawable folder with name of ic_cdv_splashscreen.xml
|
||||
// delete ic_cdv_splashscreen.png from mipmap folder
|
||||
// update themes.xml windowSplashScreenAnimatedIcon value to @drawable/ic_cdv_splashscreen
|
||||
// update cdv_themes.xml windowSplashScreenAnimatedIcon value to @drawable/ic_cdv_splashscreen
|
||||
cleanupAndSetProjectSplashScreenImage(cdvConfigPrefValue, destFilePath, possiblePreviousDestFilePath);
|
||||
} else {
|
||||
// use the default destFilePath & possiblePreviousDestFilePath, no update require.
|
||||
@@ -747,9 +747,24 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
|
||||
foreground = android_icons[density].foreground;
|
||||
monochrome = android_icons[density].monochrome;
|
||||
|
||||
const isAdaptiveIcon = background && foreground;
|
||||
const isMonochromeIcon = monochrome && isAdaptiveIcon;
|
||||
if (!isMonochromeIcon || !isAdaptiveIcon) {
|
||||
const hasAdaptiveIcons = !!background && !!foreground;
|
||||
let hasMonochromeIcon = !!monochrome;
|
||||
|
||||
if (hasMonochromeIcon && !hasAdaptiveIcons) {
|
||||
// If we have a monochrome icon, but no adaptive icons,
|
||||
// then warn that in order to use monochrome, the adaptive icons
|
||||
// must be supplied. We will ignore monochrome and proceed with the
|
||||
// icon preparation however.
|
||||
hasMonochromeIcon = false;
|
||||
monochrome = undefined;
|
||||
events.emit('warn', dedent`
|
||||
Monochrome icon found but without adaptive properties.
|
||||
Monochrome icon requires the adaptive background and foreground assets.
|
||||
See https://cordova.apache.org/docs/en/latest/config_ref/images.html fore more information.
|
||||
`);
|
||||
}
|
||||
|
||||
if (!hasAdaptiveIcons) {
|
||||
// This icon isn't an adaptive icon, so skip it
|
||||
continue;
|
||||
}
|
||||
@@ -780,7 +795,7 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
|
||||
resourceMap[targetPathForeground] = android_icons[density].foreground;
|
||||
}
|
||||
|
||||
if (monochrome) {
|
||||
if (hasMonochromeIcon) {
|
||||
if (path.extname(path.basename(monochrome)) === '.xml') {
|
||||
// Vector Use Case
|
||||
targetPathMonochrome = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher_monochrome.xml', path.basename(android_icons[density].monochrome));
|
||||
@@ -794,19 +809,23 @@ function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformReso
|
||||
|
||||
// create an XML for DPI and set color
|
||||
let icLauncherTemplate = '';
|
||||
if (monochrome) {
|
||||
icLauncherTemplate = `<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="` + backgroundVal + `" />
|
||||
<foreground android:drawable="` + foregroundVal + `" />
|
||||
<monochrome android:drawable="` + monochromeVal + `" />
|
||||
</adaptive-icon>`;
|
||||
if (hasMonochromeIcon) {
|
||||
icLauncherTemplate = dedent`
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="${backgroundVal}" />
|
||||
<foreground android:drawable="${foregroundVal}" />
|
||||
<monochrome android:drawable="${monochromeVal}" />
|
||||
</adaptive-icon>
|
||||
`;
|
||||
} else {
|
||||
icLauncherTemplate = `<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="` + backgroundVal + `" />
|
||||
<foreground android:drawable="` + foregroundVal + `" />
|
||||
</adaptive-icon>`;
|
||||
icLauncherTemplate = dedent`
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="${backgroundVal}" />
|
||||
<foreground android:drawable="${foregroundVal}" />
|
||||
</adaptive-icon>
|
||||
`;
|
||||
}
|
||||
|
||||
const launcherXmlPath = path.join(platformResourcesDir, 'mipmap-' + density + '-v26', 'ic_launcher.xml');
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const { inspect } = require('util');
|
||||
const { inspect } = require('node:util');
|
||||
const execa = require('execa');
|
||||
const Adb = require('./Adb');
|
||||
const build = require('./build');
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
|
||||
// TODO: Perhaps this should live in cordova-common?
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const fs = require('node:fs');
|
||||
const which = require('which');
|
||||
const os = require('os');
|
||||
const os = require('node:os');
|
||||
|
||||
/**
|
||||
* Reads, searches, and replaces the found occurences with replacementString and then writes the file back out.
|
||||
|
||||
@@ -15,13 +15,5 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
extends: '@cordova/eslint-config/node'
|
||||
|
||||
overrides:
|
||||
- files: [spec/**/*.js]
|
||||
extends: '@cordova/eslint-config/node-tests'
|
||||
rules:
|
||||
prefer-promise-reject-errors: off
|
||||
|
||||
- files: [cordova-js-src/**/*.js]
|
||||
extends: '@cordova/eslint-config/browser'
|
||||
# Empty for the release audit workflow.
|
||||
# The `license-config` is required even if there are no custom configs
|
||||
8943
package-lock.json
generated
8943
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
38
package.json
38
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cordova-android",
|
||||
"version": "12.0.0",
|
||||
"version": "15.0.0-dev",
|
||||
"description": "cordova-android release",
|
||||
"types": "./types/index.d.ts",
|
||||
"main": "lib/Api.js",
|
||||
@@ -14,43 +14,43 @@
|
||||
"scripts": {
|
||||
"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": "eslint .",
|
||||
"lint:fix": "npm run lint -- --fix",
|
||||
"unit-tests": "jasmine --config=spec/unit/jasmine.json",
|
||||
"cover": "nyc jasmine --config=spec/coverage.json",
|
||||
"cover": "c8 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.8.1",
|
||||
"cordova-common": "^5.0.0",
|
||||
"android-versions": "^2.1.0",
|
||||
"cordova-common": "^6.0.0",
|
||||
"dedent": "^1.7.0",
|
||||
"execa": "^5.1.1",
|
||||
"fast-glob": "^3.2.12",
|
||||
"fs-extra": "^11.1.1",
|
||||
"fast-glob": "^3.3.3",
|
||||
"is-path-inside": "^3.0.3",
|
||||
"nopt": "^7.1.0",
|
||||
"properties-parser": "^0.3.1",
|
||||
"semver": "^7.3.8",
|
||||
"nopt": "^8.1.0",
|
||||
"properties-parser": "^0.6.0",
|
||||
"semver": "^7.7.1",
|
||||
"string-argv": "^0.3.1",
|
||||
"untildify": "^4.0.0",
|
||||
"which": "^3.0.0"
|
||||
"which": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cordova/eslint-config": "^5.0.0",
|
||||
"@cordova/eslint-config": "^6.0.0",
|
||||
"c8": "^10.1.3",
|
||||
"cordova-js": "^6.1.0",
|
||||
"elementtree": "^0.1.7",
|
||||
"jasmine": "^4.6.0",
|
||||
"jasmine": "^5.10.0",
|
||||
"jasmine-spec-reporter": "^7.0.0",
|
||||
"nyc": "^15.1.0",
|
||||
"rewire": "^6.0.0"
|
||||
"rewire": "^9.0.1",
|
||||
"tmp": "^0.2.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.13.0"
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"nyc": {
|
||||
"c8": {
|
||||
"include": [
|
||||
"lib"
|
||||
],
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const os = require('os');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const os = require('node:os');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { EventEmitter } = require('events');
|
||||
const { ConfigParser, PluginInfoProvider } = require('cordova-common');
|
||||
const Api = require('../../lib/Api');
|
||||
@@ -49,14 +49,15 @@ describe('E2E', function () {
|
||||
api = await makeProject(projectPath);
|
||||
});
|
||||
afterEach(() => {
|
||||
fs.removeSync(tmpDir);
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('loads the API from a project directory', async () => {
|
||||
// Allow test project to find the `cordova-android` module
|
||||
fs.ensureSymlinkSync(
|
||||
path.join(__dirname, '../..'),
|
||||
path.join(tmpDir, 'node_modules/cordova-android'),
|
||||
fs.mkdirSync(path.join(tmpDir, 'node_modules'), { recursive: true });
|
||||
fs.symlinkSync(
|
||||
path.join(__dirname, '..', '..'),
|
||||
path.join(tmpDir, 'node_modules', 'cordova-android'),
|
||||
'junction'
|
||||
);
|
||||
|
||||
|
||||
@@ -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,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.
|
||||
|
||||
#Mon Dec 28 10:00:20 PST 2015
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const fs = require('node:fs');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const rewire = require('rewire');
|
||||
|
||||
describe('AndroidManifest', () => {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const path = require('node:path');
|
||||
const rewire = require('rewire');
|
||||
const MockCordovaGradleConfigParser = require('./mocks/config/MockCordovaGradleConfigParser');
|
||||
const CordovaGradleConfigParserFactory = require('../../lib/config/CordovaGradleConfigParserFactory');
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const common = require('cordova-common');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const rewire = require('rewire');
|
||||
|
||||
describe('android_sdk', () => {
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const rewire = require('rewire');
|
||||
const { isWindows } = require('../../../lib/utils');
|
||||
|
||||
describe('ProjectBuilder', () => {
|
||||
const rootDir = '/root';
|
||||
@@ -128,19 +129,24 @@ 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));
|
||||
it('should run gradle wrapper 8.13', async () => {
|
||||
await builder.installGradleWrapper('8.13');
|
||||
expect(execaSpy).toHaveBeenCalledWith('gradle', ['-p', path.normalize('/root/tools'), 'wrapper', '--gradle-version', '8.13'], jasmine.any(Object));
|
||||
});
|
||||
|
||||
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.13');
|
||||
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));
|
||||
});
|
||||
});
|
||||
|
||||
describe('build', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(builder, 'getArgs');
|
||||
@@ -170,7 +176,12 @@ 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', () => {
|
||||
@@ -209,7 +220,7 @@ describe('ProjectBuilder', () => {
|
||||
beforeEach(() => {
|
||||
const marker = ProjectBuilder.__get__('MARKER');
|
||||
spyOn(fs, 'readFileSync').and.returnValue(`Some Header Here: ${marker}`);
|
||||
spyOn(fs, 'removeSync');
|
||||
spyOn(fs, 'rmSync');
|
||||
spyOn(builder, 'getArgs');
|
||||
execaSpy.and.returnValue(Promise.resolve());
|
||||
});
|
||||
@@ -227,14 +238,19 @@ 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());
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove "out" folder', () => {
|
||||
return builder.clean({}).then(() => {
|
||||
expect(fs.removeSync).toHaveBeenCalledWith(path.join(rootDir, 'out'));
|
||||
expect(fs.rmSync).toHaveBeenCalledWith(path.join(rootDir, 'out'), { recursive: true, force: true });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -245,8 +261,8 @@ describe('ProjectBuilder', () => {
|
||||
spyOn(fs, 'existsSync').and.returnValue(true);
|
||||
|
||||
return builder.clean({}).then(() => {
|
||||
expect(fs.removeSync).toHaveBeenCalledWith(debugSigningFile);
|
||||
expect(fs.removeSync).toHaveBeenCalledWith(releaseSigningFile);
|
||||
expect(fs.rmSync).toHaveBeenCalledWith(debugSigningFile);
|
||||
expect(fs.rmSync).toHaveBeenCalledWith(releaseSigningFile);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -257,8 +273,8 @@ describe('ProjectBuilder', () => {
|
||||
spyOn(fs, 'existsSync').and.returnValue(false);
|
||||
|
||||
return builder.clean({}).then(() => {
|
||||
expect(fs.removeSync).not.toHaveBeenCalledWith(debugSigningFile);
|
||||
expect(fs.removeSync).not.toHaveBeenCalledWith(releaseSigningFile);
|
||||
expect(fs.rmSync).not.toHaveBeenCalledWith(debugSigningFile);
|
||||
expect(fs.rmSync).not.toHaveBeenCalledWith(releaseSigningFile);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
|
||||
const rewire = require('rewire');
|
||||
const android_sdk = require('../../lib/android_sdk');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const events = require('cordova-common').events;
|
||||
const which = require('which');
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ const rewire = require('rewire');
|
||||
const utils = require('../../lib/utils');
|
||||
const create = rewire('../../lib/create');
|
||||
const check_reqs = require('../../lib/check_reqs');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const MockCordovaGradleConfigParser = require('./mocks/config/MockCordovaGradleConfigParser');
|
||||
const CordovaGradleConfigParserFactory = require('../../lib/config/CordovaGradleConfigParserFactory');
|
||||
|
||||
@@ -142,8 +142,8 @@ describe('create', function () {
|
||||
spyOn(create, 'prepBuildFiles');
|
||||
revert_manifest_mock = create.__set__('AndroidManifest', Manifest_mock);
|
||||
spyOn(fs, 'existsSync').and.returnValue(false);
|
||||
spyOn(fs, 'copySync');
|
||||
spyOn(fs, 'ensureDirSync');
|
||||
spyOn(fs, 'cpSync');
|
||||
spyOn(fs, 'mkdirSync');
|
||||
spyOn(utils, 'replaceFileContents');
|
||||
config_mock = jasmine.createSpyObj('ConfigParser mock instance', ['packageName', 'android_packageName', 'name', 'android_activityName']);
|
||||
events_mock = jasmine.createSpyObj('EventEmitter mock instance', ['emit']);
|
||||
@@ -155,10 +155,10 @@ describe('create', function () {
|
||||
});
|
||||
|
||||
describe('parameter values and defaults', function () {
|
||||
it('should have a default package name of io.cordova.helloCordova', () => {
|
||||
it('should have a default package name of org.apache.cordova.hellocordova', () => {
|
||||
config_mock.packageName.and.returnValue(undefined);
|
||||
return create.create(project_path, config_mock, {}, events_mock).then(() => {
|
||||
expect(create.validatePackageName).toHaveBeenCalledWith('io.cordova.helloCordova');
|
||||
expect(create.validatePackageName).toHaveBeenCalledWith('org.apache.cordova.hellocordova');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -238,17 +238,17 @@ describe('create', function () {
|
||||
describe('happy path', function () {
|
||||
it('should copy project templates from a specified custom template', () => {
|
||||
return create.create(project_path, config_mock, { customTemplate: '/template/path' }, events_mock).then(() => {
|
||||
expect(fs.copySync).toHaveBeenCalledWith(path.join('/template/path', 'assets'), path.join(app_path, 'assets'));
|
||||
expect(fs.copySync).toHaveBeenCalledWith(path.join('/template/path', 'res'), path.join(app_path, 'res'));
|
||||
expect(fs.copySync).toHaveBeenCalledWith(path.join('/template/path', 'gitignore'), path.join(project_path, '.gitignore'));
|
||||
expect(fs.cpSync).toHaveBeenCalledWith(path.join('/template/path', 'assets'), path.join(app_path, 'assets'), { recursive: true });
|
||||
expect(fs.cpSync).toHaveBeenCalledWith(path.join('/template/path', 'res'), path.join(app_path, 'res'), { recursive: true });
|
||||
expect(fs.cpSync).toHaveBeenCalledWith(path.join('/template/path', 'gitignore'), path.join(project_path, '.gitignore'));
|
||||
});
|
||||
});
|
||||
|
||||
it('should copy project templates from the default templates location if no custom template is provided', () => {
|
||||
return create.create(project_path, config_mock, {}, events_mock).then(() => {
|
||||
expect(fs.copySync).toHaveBeenCalledWith(path.join(default_templates, 'assets'), path.join(app_path, 'assets'));
|
||||
expect(fs.copySync).toHaveBeenCalledWith(path.join(default_templates, 'res'), path.join(app_path, 'res'));
|
||||
expect(fs.copySync).toHaveBeenCalledWith(path.join(default_templates, 'gitignore'), path.join(project_path, '.gitignore'));
|
||||
expect(fs.cpSync).toHaveBeenCalledWith(path.join(default_templates, 'assets'), path.join(app_path, 'assets'), { recursive: true });
|
||||
expect(fs.cpSync).toHaveBeenCalledWith(path.join(default_templates, 'res'), path.join(app_path, 'res'), { recursive: true });
|
||||
expect(fs.cpSync).toHaveBeenCalledWith(path.join(default_templates, 'gitignore'), path.join(project_path, '.gitignore'));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -261,7 +261,7 @@ describe('create', function () {
|
||||
it('should create a java src directory based on the provided project package name', () => {
|
||||
config_mock.packageName.and.returnValue('org.apache.cordova');
|
||||
return create.create(project_path, config_mock, {}, events_mock).then(() => {
|
||||
expect(fs.ensureDirSync).toHaveBeenCalledWith(path.join(app_path, 'java', 'org', 'apache', 'cordova'));
|
||||
expect(fs.mkdirSync).toHaveBeenCalledWith(path.join(app_path, 'java', 'org', 'apache', 'cordova'), { recursive: true });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -270,23 +270,23 @@ describe('create', function () {
|
||||
config_mock.android_activityName.and.returnValue('CEEDEEVEE');
|
||||
const activity_path = path.join(app_path, 'java', 'org', 'apache', 'cordova', 'CEEDEEVEE.java');
|
||||
return create.create(project_path, config_mock, {}, events_mock).then(() => {
|
||||
expect(fs.copySync).toHaveBeenCalledWith(path.join(default_templates, 'Activity.java'), activity_path);
|
||||
expect(fs.cpSync).toHaveBeenCalledWith(path.join(default_templates, 'Activity.java'), activity_path);
|
||||
expect(utils.replaceFileContents).toHaveBeenCalledWith(activity_path, /__ACTIVITY__/, 'CEEDEEVEE');
|
||||
expect(utils.replaceFileContents).toHaveBeenCalledWith(activity_path, /__ID__/, 'org.apache.cordova');
|
||||
});
|
||||
});
|
||||
|
||||
it('should interpolate the project name into strings.xml', () => {
|
||||
it('should interpolate the project name into cdv_strings.xml', () => {
|
||||
config_mock.name.and.returnValue('IncredibleApp');
|
||||
return create.create(project_path, config_mock, {}, events_mock).then(() => {
|
||||
expect(utils.replaceFileContents).toHaveBeenCalledWith(path.join(app_path, 'res', 'values', 'strings.xml'), /__NAME__/, 'IncredibleApp');
|
||||
expect(utils.replaceFileContents).toHaveBeenCalledWith(path.join(app_path, 'res', 'values', 'cdv_strings.xml'), /__NAME__/, 'IncredibleApp');
|
||||
});
|
||||
});
|
||||
|
||||
it('should interpolate the escaped project name into strings.xml', () => {
|
||||
it('should interpolate the escaped project name into cdv_strings.xml', () => {
|
||||
config_mock.name.and.returnValue('<Incredible&App>');
|
||||
return create.create(project_path, config_mock, {}, events_mock).then(() => {
|
||||
expect(utils.replaceFileContents).toHaveBeenCalledWith(path.join(app_path, 'res', 'values', 'strings.xml'), /__NAME__/, '<Incredible&App>');
|
||||
expect(utils.replaceFileContents).toHaveBeenCalledWith(path.join(app_path, 'res', 'values', 'cdv_strings.xml'), /__NAME__/, '<Incredible&App>');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const rewire = require('rewire');
|
||||
const which = require('which');
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const path = require('node:path');
|
||||
const rewire = require('rewire');
|
||||
const { CordovaError } = require('cordova-common');
|
||||
const utils = require('../../lib/utils');
|
||||
|
||||
@@ -18,27 +18,41 @@
|
||||
|
||||
const rewire = require('rewire');
|
||||
const common = rewire('../../../lib/pluginHandlers');
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const osenv = require('os');
|
||||
const path = require('node:path');
|
||||
const fs = require('node:fs');
|
||||
const tmp = require('tmp');
|
||||
|
||||
const test_dir = path.join(osenv.tmpdir(), 'test_plugman');
|
||||
tmp.setGracefulCleanup();
|
||||
|
||||
const tempdir = tmp.dirSync({ unsafeCleanup: true });
|
||||
const test_dir = path.join(tempdir.name, 'test_plugman');
|
||||
const project_dir = path.join(test_dir, 'project');
|
||||
const src = path.join(project_dir, 'src');
|
||||
const dest = path.join(project_dir, 'dest');
|
||||
const java_dir = path.join(src, 'one', 'two', 'three');
|
||||
const java_file = path.join(java_dir, 'test.java');
|
||||
const symlink_file = path.join(java_dir, 'symlink');
|
||||
const non_plugin_file = path.join(osenv.tmpdir(), 'non_plugin_file');
|
||||
const non_plugin_file = path.join(tempdir.name, 'non_plugin_file');
|
||||
|
||||
const copyFile = common.__get__('copyFile');
|
||||
const deleteJava = common.__get__('deleteJava');
|
||||
const copyNewFile = common.__get__('copyNewFile');
|
||||
|
||||
function outputFileSync (file, content) {
|
||||
const dir = path.dirname(file);
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
fs.writeFileSync(file, content, 'utf-8');
|
||||
}
|
||||
|
||||
describe('common platform handler', function () {
|
||||
afterAll(() => {
|
||||
// Remove tempdir after all specs complete
|
||||
fs.rmSync(tempdir.name, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.removeSync(test_dir);
|
||||
fs.removeSync(non_plugin_file);
|
||||
fs.rmSync(test_dir, { recursive: true, force: true });
|
||||
fs.rmSync(non_plugin_file, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
describe('copyFile', function () {
|
||||
@@ -48,15 +62,15 @@ describe('common platform handler', function () {
|
||||
});
|
||||
|
||||
it('Test#002 : should throw if src not in plugin directory', function () {
|
||||
fs.ensureDirSync(project_dir);
|
||||
fs.outputFileSync(non_plugin_file, 'contents');
|
||||
fs.mkdirSync(project_dir, { recursive: true });
|
||||
outputFileSync(non_plugin_file, 'contents');
|
||||
const outside_file = '../non_plugin_file';
|
||||
expect(function () { copyFile(test_dir, outside_file, project_dir, dest); })
|
||||
.toThrow(new Error('File "' + path.resolve(test_dir, outside_file) + '" is located outside the plugin directory "' + test_dir + '"'));
|
||||
});
|
||||
|
||||
it('Test#003 : should allow symlink src, if inside plugin', function () {
|
||||
fs.outputFileSync(java_file, 'contents');
|
||||
outputFileSync(java_file, 'contents');
|
||||
|
||||
// This will fail on windows if not admin - ignore the error in that case.
|
||||
if (ignoreEPERMonWin32(java_file, symlink_file)) {
|
||||
@@ -67,8 +81,8 @@ describe('common platform handler', function () {
|
||||
});
|
||||
|
||||
it('Test#004 : should throw if symlink is linked to a file outside the plugin', function () {
|
||||
fs.ensureDirSync(java_dir);
|
||||
fs.outputFileSync(non_plugin_file, 'contents');
|
||||
fs.mkdirSync(java_dir, { recursive: true });
|
||||
outputFileSync(non_plugin_file, 'contents');
|
||||
|
||||
// This will fail on windows if not admin - ignore the error in that case.
|
||||
if (ignoreEPERMonWin32(non_plugin_file, symlink_file)) {
|
||||
@@ -80,37 +94,37 @@ describe('common platform handler', function () {
|
||||
});
|
||||
|
||||
it('Test#005 : should throw if dest is outside the project directory', function () {
|
||||
fs.outputFileSync(java_file, 'contents');
|
||||
outputFileSync(java_file, 'contents');
|
||||
expect(function () { copyFile(test_dir, java_file, project_dir, non_plugin_file); })
|
||||
.toThrow(new Error('Destination "' + path.resolve(project_dir, non_plugin_file) + '" for source file "' + path.resolve(test_dir, java_file) + '" is located outside the project'));
|
||||
});
|
||||
|
||||
it('Test#006 : should call mkdir -p on target path', function () {
|
||||
fs.outputFileSync(java_file, 'contents');
|
||||
it('Test#006 : should call mkdirSync target path', function () {
|
||||
outputFileSync(java_file, 'contents');
|
||||
|
||||
const s = spyOn(fs, 'ensureDirSync').and.callThrough();
|
||||
const s = spyOn(fs, 'mkdirSync').and.callThrough();
|
||||
const resolvedDest = path.resolve(project_dir, dest);
|
||||
|
||||
copyFile(test_dir, java_file, project_dir, dest);
|
||||
|
||||
expect(s).toHaveBeenCalled();
|
||||
expect(s).toHaveBeenCalledWith(path.dirname(resolvedDest));
|
||||
expect(s).toHaveBeenCalledWith(path.dirname(resolvedDest), { recursive: true });
|
||||
});
|
||||
|
||||
it('Test#007 : should call cp source/dest paths', function () {
|
||||
fs.outputFileSync(java_file, 'contents');
|
||||
outputFileSync(java_file, 'contents');
|
||||
|
||||
const s = spyOn(fs, 'copySync').and.callThrough();
|
||||
const s = spyOn(fs, 'cpSync').and.callThrough();
|
||||
const resolvedDest = path.resolve(project_dir, dest);
|
||||
|
||||
copyFile(test_dir, java_file, project_dir, dest);
|
||||
|
||||
expect(s).toHaveBeenCalled();
|
||||
expect(s).toHaveBeenCalledWith(java_file, resolvedDest);
|
||||
expect(s).toHaveBeenCalledWith(java_file, resolvedDest, { recursive: true });
|
||||
});
|
||||
|
||||
it('should handle relative paths when checking for sub paths', () => {
|
||||
fs.outputFileSync(java_file, 'contents');
|
||||
outputFileSync(java_file, 'contents');
|
||||
const relativeProjectPath = path.relative(process.cwd(), project_dir);
|
||||
|
||||
expect(() => {
|
||||
@@ -121,7 +135,7 @@ describe('common platform handler', function () {
|
||||
|
||||
describe('copyNewFile', function () {
|
||||
it('Test#008 : should throw if target path exists', function () {
|
||||
fs.ensureDirSync(dest);
|
||||
fs.mkdirSync(dest, { recursive: true });
|
||||
expect(function () { copyNewFile(test_dir, src, project_dir, dest); })
|
||||
.toThrow(new Error('"' + dest + '" already exists!'));
|
||||
});
|
||||
@@ -129,14 +143,14 @@ describe('common platform handler', function () {
|
||||
|
||||
describe('deleteJava', function () {
|
||||
beforeEach(function () {
|
||||
fs.outputFileSync(java_file, 'contents');
|
||||
outputFileSync(java_file, 'contents');
|
||||
});
|
||||
|
||||
it('Test#009 : should call fs.unlinkSync on the provided paths', function () {
|
||||
const s = spyOn(fs, 'removeSync').and.callThrough();
|
||||
const s = spyOn(fs, 'rmSync').and.callThrough();
|
||||
deleteJava(project_dir, java_file);
|
||||
expect(s).toHaveBeenCalled();
|
||||
expect(s).toHaveBeenCalledWith(path.resolve(project_dir, java_file));
|
||||
expect(s).toHaveBeenCalledWith(path.resolve(project_dir, java_file), { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('Test#010 : should delete empty directories after removing source code in a java src path hierarchy', function () {
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
const rewire = require('rewire');
|
||||
const common = rewire('../../../lib/pluginHandlers');
|
||||
const android = common.__get__('handlers');
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const os = require('os');
|
||||
const path = require('node:path');
|
||||
const fs = require('node:fs');
|
||||
const os = require('node:os');
|
||||
const temp = path.join(os.tmpdir(), 'plugman');
|
||||
const plugins_dir = path.join(temp, 'cordova/plugins');
|
||||
const dummyplugin = path.join(__dirname, '../../fixtures/org.test.plugins.dummyplugin');
|
||||
@@ -56,14 +56,14 @@ describe('android project handler', function () {
|
||||
let dummyProject;
|
||||
|
||||
beforeEach(function () {
|
||||
fs.ensureDirSync(temp);
|
||||
fs.mkdirSync(temp, { recursive: true });
|
||||
dummyProject = AndroidProject.getProjectFile(temp);
|
||||
copyFileSpy.calls.reset();
|
||||
common.__set__('copyFile', copyFileSpy);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
fs.removeSync(temp);
|
||||
fs.rmSync(temp, { recursive: true, force: true });
|
||||
common.__set__('copyFile', copyFileOrig);
|
||||
});
|
||||
|
||||
@@ -83,7 +83,7 @@ describe('android project handler', function () {
|
||||
|
||||
describe('of <source-file> elements', function () {
|
||||
beforeEach(function () {
|
||||
fs.copySync(android_studio_project, temp);
|
||||
fs.cpSync(android_studio_project, temp, { recursive: true });
|
||||
});
|
||||
|
||||
it('Test#003 : should copy stuff from one location to another by calling common.copyFile', function () {
|
||||
@@ -102,7 +102,7 @@ describe('android project handler', function () {
|
||||
it('Test#006 : should throw if target file already exists', function () {
|
||||
// write out a file
|
||||
let target = path.resolve(temp, 'app', 'src', 'main', 'java', 'com', 'phonegap', 'plugins', 'dummyplugin');
|
||||
fs.ensureDirSync(target);
|
||||
fs.mkdirSync(target, { recursive: true });
|
||||
target = path.join(target, 'DummyPlugin.java');
|
||||
fs.writeFileSync(target, 'some bs', 'utf-8');
|
||||
|
||||
@@ -113,73 +113,73 @@ describe('android project handler', function () {
|
||||
|
||||
// TODO: renumber these tests and other tests below
|
||||
it('Test#00a6 : should allow installing sources with new app target-dir scheme', function () {
|
||||
android['source-file'].install(valid_source[1], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].install(valid_source[1], dummyPluginInfo, dummyProject);
|
||||
expect(copyFileSpy)
|
||||
.toHaveBeenCalledWith(dummyplugin, 'src/android/DummyPlugin2.java', temp, path.join('app/src/main/src/com/phonegap/plugins/dummyplugin/DummyPlugin2.java'), false);
|
||||
});
|
||||
|
||||
it('Test#006b : should allow installing jar lib file from sources with new app target-dir scheme', function () {
|
||||
android['source-file'].install(valid_source[2], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].install(valid_source[2], dummyPluginInfo, dummyProject);
|
||||
expect(copyFileSpy)
|
||||
.toHaveBeenCalledWith(dummyplugin, 'src/android/TestLib.jar', temp, path.join('app/libs/TestLib.jar'), false);
|
||||
});
|
||||
|
||||
it('Test#006c : should allow installing aar lib file from sources with new app target-dir scheme', function () {
|
||||
android['source-file'].install(valid_source[3], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].install(valid_source[3], dummyPluginInfo, dummyProject);
|
||||
expect(copyFileSpy)
|
||||
.toHaveBeenCalledWith(dummyplugin, 'src/android/TestAar.aar', temp, path.join('app/libs/TestAar.aar'), false);
|
||||
});
|
||||
|
||||
it('Test#006d : should allow installing xml file from sources with old target-dir scheme', function () {
|
||||
android['source-file'].install(valid_source[4], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].install(valid_source[4], dummyPluginInfo, dummyProject);
|
||||
expect(copyFileSpy).toHaveBeenCalledWith(dummyplugin,
|
||||
'src/android/mysettings.xml', temp,
|
||||
path.join('app/src/main/res/xml/mysettings.xml'), false);
|
||||
});
|
||||
|
||||
it('Test#006e : should allow installing file with other extension from sources with old target-dir scheme', function () {
|
||||
android['source-file'].install(valid_source[5], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].install(valid_source[5], dummyPluginInfo, dummyProject);
|
||||
expect(copyFileSpy).toHaveBeenCalledWith(dummyplugin,
|
||||
'src/android/other.extension', temp,
|
||||
path.join('app/src/main/res/values/other.extension'), false);
|
||||
});
|
||||
|
||||
it('Test#006f : should allow installing aidl file from sources with old target-dir scheme (GH-547)', function () {
|
||||
android['source-file'].install(valid_source[6], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].install(valid_source[6], dummyPluginInfo, dummyProject);
|
||||
expect(copyFileSpy).toHaveBeenCalledWith(dummyplugin,
|
||||
'src/android/myapi.aidl', temp,
|
||||
path.join('app/src/main/aidl/com/mytest/myapi.aidl'), false);
|
||||
});
|
||||
|
||||
it('Test#006g : should allow installing aar lib file from sources with old target-dir scheme (GH-547)', function () {
|
||||
android['source-file'].install(valid_source[7], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].install(valid_source[7], dummyPluginInfo, dummyProject);
|
||||
expect(copyFileSpy).toHaveBeenCalledWith(dummyplugin,
|
||||
'src/android/testaar2.aar', temp,
|
||||
path.join('app/libs/testaar2.aar'), false);
|
||||
});
|
||||
|
||||
it('Test#006h : should allow installing jar lib file from sources with old target-dir scheme (GH-547)', function () {
|
||||
android['source-file'].install(valid_source[8], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].install(valid_source[8], dummyPluginInfo, dummyProject);
|
||||
expect(copyFileSpy).toHaveBeenCalledWith(dummyplugin,
|
||||
'src/android/testjar2.jar', temp,
|
||||
path.join('app/libs/testjar2.jar'), false);
|
||||
});
|
||||
|
||||
it('Test#006i : should allow installing .so lib file from sources with old target-dir scheme (GH-547)', function () {
|
||||
android['source-file'].install(valid_source[9], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].install(valid_source[9], dummyPluginInfo, dummyProject);
|
||||
expect(copyFileSpy).toHaveBeenCalledWith(dummyplugin,
|
||||
'src/android/jniLibs/x86/libnative.so', temp,
|
||||
path.join('app/src/main/jniLibs/x86/libnative.so'), false);
|
||||
});
|
||||
|
||||
it('Test#006j : should allow installing sources with target-dir that includes "app"', function () {
|
||||
android['source-file'].install(valid_source[10], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].install(valid_source[10], dummyPluginInfo, dummyProject);
|
||||
expect(copyFileSpy)
|
||||
.toHaveBeenCalledWith(dummyplugin, 'src/android/DummyPlugin2.java', temp, path.join('app/src/main/java/com/appco/DummyPlugin2.java'), false);
|
||||
});
|
||||
|
||||
it('Test#006k : should allow installing sources with target-dir that includes "app" in its first directory', function () {
|
||||
android['source-file'].install(valid_source[11], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].install(valid_source[11], dummyPluginInfo, dummyProject);
|
||||
expect(copyFileSpy)
|
||||
.toHaveBeenCalledWith(dummyplugin, 'src/android/DummyPlugin2.java', temp, path.join('app/src/main/java/appco/src/DummyPlugin2.java'), false);
|
||||
});
|
||||
@@ -192,7 +192,7 @@ describe('android project handler', function () {
|
||||
const copyNewFileSpy = jasmine.createSpy('copyNewFile');
|
||||
|
||||
beforeEach(function () {
|
||||
fs.copySync(android_studio_project, temp);
|
||||
fs.cpSync(android_studio_project, temp, { recursive: true });
|
||||
|
||||
spyOn(dummyProject, 'addSystemLibrary');
|
||||
spyOn(dummyProject, 'addSubProject');
|
||||
@@ -222,7 +222,7 @@ describe('android project handler', function () {
|
||||
|
||||
it('Test#010 : should not copy anything if "custom" attribute is not set', function () {
|
||||
const framework = { src: 'plugin-lib' };
|
||||
const cpSpy = spyOn(fs, 'copySync');
|
||||
const cpSpy = spyOn(fs, 'cpSync');
|
||||
android.framework.install(framework, dummyPluginInfo, dummyProject);
|
||||
expect(dummyProject.addSystemLibrary).toHaveBeenCalledWith(someString, framework.src);
|
||||
expect(cpSpy).not.toHaveBeenCalled();
|
||||
@@ -289,23 +289,23 @@ describe('android project handler', function () {
|
||||
|
||||
describe('uninstallation', function () {
|
||||
const deleteJavaOrig = common.__get__('deleteJava');
|
||||
const originalRemoveSync = fs.removeSync;
|
||||
const originalRmSync = fs.rmSync;
|
||||
const deleteJavaSpy = jasmine.createSpy('deleteJava');
|
||||
let dummyProject;
|
||||
let removeSyncSpy;
|
||||
let rmSyncSpy;
|
||||
|
||||
beforeEach(function () {
|
||||
fs.ensureDirSync(temp);
|
||||
fs.ensureDirSync(plugins_dir);
|
||||
fs.copySync(android_studio_project, temp);
|
||||
fs.mkdirSync(temp, { recursive: true });
|
||||
fs.mkdirSync(plugins_dir, { recursive: true });
|
||||
fs.cpSync(android_studio_project, temp, { recursive: true });
|
||||
AndroidProject.purgeCache();
|
||||
dummyProject = AndroidProject.getProjectFile(temp);
|
||||
removeSyncSpy = spyOn(fs, 'removeSync');
|
||||
rmSyncSpy = spyOn(fs, 'rmSync');
|
||||
common.__set__('deleteJava', deleteJavaSpy);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
originalRemoveSync.call(fs, temp);
|
||||
originalRmSync.call(fs, temp, { recursive: true });
|
||||
common.__set__('deleteJava', deleteJavaOrig);
|
||||
});
|
||||
|
||||
@@ -313,7 +313,7 @@ describe('android project handler', function () {
|
||||
it('Test#017 : should remove jar files for Android Studio projects', function () {
|
||||
android['lib-file'].install(valid_libs[0], dummyPluginInfo, dummyProject);
|
||||
android['lib-file'].uninstall(valid_libs[0], dummyPluginInfo, dummyProject);
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app/libs/TestLib.jar'));
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app/libs/TestLib.jar'), { recursive: true, force: true });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -321,7 +321,7 @@ describe('android project handler', function () {
|
||||
it('Test#018 : should remove files for Android Studio projects', function () {
|
||||
android['resource-file'].install(valid_resources[0], dummyPluginInfo, dummyProject);
|
||||
android['resource-file'].uninstall(valid_resources[0], dummyPluginInfo, dummyProject);
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app', 'src', 'main', 'res', 'xml', 'dummy.xml'));
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app', 'src', 'main', 'res', 'xml', 'dummy.xml'), { recursive: true, force: true });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -333,62 +333,62 @@ describe('android project handler', function () {
|
||||
});
|
||||
|
||||
it('Test#019a : should remove stuff by calling common.deleteJava for Android Studio projects, with specific app target-dir', function () {
|
||||
android['source-file'].install(valid_source[1], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].uninstall(valid_source[1], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].install(valid_source[1], dummyPluginInfo, dummyProject);
|
||||
android['source-file'].uninstall(valid_source[1], dummyPluginInfo, dummyProject);
|
||||
expect(deleteJavaSpy).toHaveBeenCalledWith(temp, path.join('app/src/main/src/com/phonegap/plugins/dummyplugin/DummyPlugin2.java'));
|
||||
});
|
||||
|
||||
it('Test#019b : should remove stuff by calling common.removeFile for Android Studio projects, of jar with new app target-dir scheme', function () {
|
||||
android['source-file'].install(valid_source[2], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].uninstall(valid_source[2], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app/libs/TestLib.jar'));
|
||||
it('Test#019b : should remove stuff by calling removeFileF for Android Studio projects, of jar with new app target-dir scheme', function () {
|
||||
android['source-file'].install(valid_source[2], dummyPluginInfo, dummyProject);
|
||||
android['source-file'].uninstall(valid_source[2], dummyPluginInfo, dummyProject);
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app/libs/TestLib.jar'), { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('Test#019c : should remove stuff by calling common.removeFile for Android Studio projects, of aar with new app target-dir scheme', function () {
|
||||
android['source-file'].install(valid_source[3], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].uninstall(valid_source[3], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app/libs/TestAar.aar'));
|
||||
it('Test#019c : should remove stuff by calling removeFileF for Android Studio projects, of aar with new app target-dir scheme', function () {
|
||||
android['source-file'].install(valid_source[3], dummyPluginInfo, dummyProject);
|
||||
android['source-file'].uninstall(valid_source[3], dummyPluginInfo, dummyProject);
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app/libs/TestAar.aar'), { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('Test#019d : should remove stuff by calling common.removeFile for Android Studio projects, of xml with old target-dir scheme', function () {
|
||||
android['source-file'].install(valid_source[4], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].uninstall(valid_source[4], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app/src/main/res/xml/mysettings.xml'));
|
||||
it('Test#019d : should remove stuff by calling removeFileF for Android Studio projects, of xml with old target-dir scheme', function () {
|
||||
android['source-file'].install(valid_source[4], dummyPluginInfo, dummyProject);
|
||||
android['source-file'].uninstall(valid_source[4], dummyPluginInfo, dummyProject);
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app/src/main/res/xml/mysettings.xml'), { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('Test#019e : should remove stuff by calling common.removeFile for Android Studio projects, of file with other extension with old target-dir scheme', function () {
|
||||
android['source-file'].install(valid_source[5], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].uninstall(valid_source[5], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app/src/main/res/values/other.extension'));
|
||||
it('Test#019e : should remove stuff by calling removeFileF for Android Studio projects, of file with other extension with old target-dir scheme', function () {
|
||||
android['source-file'].install(valid_source[5], dummyPluginInfo, dummyProject);
|
||||
android['source-file'].uninstall(valid_source[5], dummyPluginInfo, dummyProject);
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app/src/main/res/values/other.extension'), { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('Test#019f : should remove stuff by calling common.removeFile for Android Studio projects, of aidl with old target-dir scheme (GH-547)', function () {
|
||||
android['source-file'].install(valid_source[6], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].uninstall(valid_source[6], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app/src/main/aidl/com/mytest/myapi.aidl'));
|
||||
it('Test#019f : should remove stuff by calling removeFileF for Android Studio projects, of aidl with old target-dir scheme (GH-547)', function () {
|
||||
android['source-file'].install(valid_source[6], dummyPluginInfo, dummyProject);
|
||||
android['source-file'].uninstall(valid_source[6], dummyPluginInfo, dummyProject);
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app/src/main/aidl/com/mytest/myapi.aidl'), { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('Test#019g : should remove stuff by calling common.removeFile for Android Studio projects, of aar with old target-dir scheme (GH-547)', function () {
|
||||
android['source-file'].install(valid_source[7], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].uninstall(valid_source[7], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app/libs/testaar2.aar'));
|
||||
it('Test#019g : should remove stuff by calling removeFileF for Android Studio projects, of aar with old target-dir scheme (GH-547)', function () {
|
||||
android['source-file'].install(valid_source[7], dummyPluginInfo, dummyProject);
|
||||
android['source-file'].uninstall(valid_source[7], dummyPluginInfo, dummyProject);
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app/libs/testaar2.aar'), { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('Test#019h : should remove stuff by calling common.removeFile for Android Studio projects, of jar with old target-dir scheme (GH-547)', function () {
|
||||
android['source-file'].install(valid_source[8], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].uninstall(valid_source[8], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app/libs/testjar2.jar'));
|
||||
it('Test#019h : should remove stuff by calling removeFileF for Android Studio projects, of jar with old target-dir scheme (GH-547)', function () {
|
||||
android['source-file'].install(valid_source[8], dummyPluginInfo, dummyProject);
|
||||
android['source-file'].uninstall(valid_source[8], dummyPluginInfo, dummyProject);
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app/libs/testjar2.jar'), { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('Test#019i : should remove stuff by calling common.removeFile for Android Studio projects, of .so lib file with old target-dir scheme (GH-547)', function () {
|
||||
android['source-file'].install(valid_source[9], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].uninstall(valid_source[9], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app/src/main/jniLibs/x86/libnative.so'));
|
||||
it('Test#019i : should remove stuff by calling removeFileF for Android Studio projects, of .so lib file with old target-dir scheme (GH-547)', function () {
|
||||
android['source-file'].install(valid_source[9], dummyPluginInfo, dummyProject);
|
||||
android['source-file'].uninstall(valid_source[9], dummyPluginInfo, dummyProject);
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(path.join(dummyProject.projectDir, 'app/src/main/jniLibs/x86/libnative.so'), { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('Test#019j : should remove stuff by calling common.deleteJava for Android Studio projects, with target-dir that includes "app"', function () {
|
||||
android['source-file'].install(valid_source[10], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].uninstall(valid_source[10], dummyPluginInfo, dummyProject, { android_studio: true });
|
||||
android['source-file'].install(valid_source[10], dummyPluginInfo, dummyProject);
|
||||
android['source-file'].uninstall(valid_source[10], dummyPluginInfo, dummyProject);
|
||||
expect(deleteJavaSpy).toHaveBeenCalledWith(dummyProject.projectDir, path.join('app/src/main/java/com/appco/DummyPlugin2.java'));
|
||||
});
|
||||
});
|
||||
@@ -397,7 +397,7 @@ describe('android project handler', function () {
|
||||
const someString = jasmine.any(String);
|
||||
|
||||
beforeEach(function () {
|
||||
fs.ensureDirSync(path.join(dummyProject.projectDir, dummyPluginInfo.id));
|
||||
fs.mkdirSync(path.join(dummyProject.projectDir, dummyPluginInfo.id), { recursive: true });
|
||||
|
||||
spyOn(dummyProject, 'removeSystemLibrary');
|
||||
spyOn(dummyProject, 'removeSubProject');
|
||||
@@ -424,13 +424,13 @@ describe('android project handler', function () {
|
||||
const framework = { src: 'plugin-lib', custom: true };
|
||||
android.framework.uninstall(framework, dummyPluginInfo, dummyProject);
|
||||
expect(dummyProject.removeSubProject).toHaveBeenCalledWith(dummyProject.projectDir, someString);
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(someString);
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(someString, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('Test#24 : should install gradleReference using project.removeGradleReference', function () {
|
||||
const framework = { src: 'plugin-lib', custom: true, type: 'gradleReference' };
|
||||
android.framework.uninstall(framework, dummyPluginInfo, dummyProject);
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(someString);
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(someString, { recursive: true, force: true });
|
||||
expect(dummyProject.removeGradleReference).toHaveBeenCalledWith(dummyProject.projectDir, someString);
|
||||
});
|
||||
});
|
||||
@@ -453,14 +453,14 @@ describe('android project handler', function () {
|
||||
|
||||
it('Test#025 : should put module to both www and platform_www when options.usePlatformWww flag is specified', function () {
|
||||
android['js-module'].uninstall(jsModule, dummyPluginInfo, dummyProject, { usePlatformWww: true });
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(wwwDest);
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(platformWwwDest);
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(wwwDest, { recursive: true, force: true });
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(platformWwwDest, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('Test#026 : should put module to www only when options.usePlatformWww flag is not specified', function () {
|
||||
android['js-module'].uninstall(jsModule, dummyPluginInfo, dummyProject);
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(wwwDest);
|
||||
expect(removeSyncSpy).not.toHaveBeenCalledWith(platformWwwDest);
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(wwwDest, { recursive: true, force: true });
|
||||
expect(rmSyncSpy).not.toHaveBeenCalledWith(platformWwwDest, { recursive: true, force: true });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -481,14 +481,14 @@ describe('android project handler', function () {
|
||||
|
||||
it('Test#027 : should put module to both www and platform_www when options.usePlatformWww flag is specified', function () {
|
||||
android.asset.uninstall(asset, dummyPluginInfo, dummyProject, { usePlatformWww: true });
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(wwwDest);
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(platformWwwDest);
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(wwwDest, { recursive: true, force: true });
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(platformWwwDest, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('Test#028 : should put module to www only when options.usePlatformWww flag is not specified', function () {
|
||||
android.asset.uninstall(asset, dummyPluginInfo, dummyProject);
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(wwwDest);
|
||||
expect(removeSyncSpy).not.toHaveBeenCalledWith(platformWwwDest);
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(wwwDest, { recursive: true, force: true });
|
||||
expect(rmSyncSpy).not.toHaveBeenCalledWith(platformWwwDest, { recursive: true, force: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
const rewire = require('rewire');
|
||||
const path = require('path');
|
||||
const path = require('node:path');
|
||||
const CordovaError = require('cordova-common').CordovaError;
|
||||
const GradlePropertiesParser = require('../../lib/config/GradlePropertiesParser');
|
||||
const utils = require('../../lib/utils');
|
||||
@@ -630,43 +630,106 @@ describe('prepare', () => {
|
||||
let preparedIcons;
|
||||
let resourceMap;
|
||||
|
||||
beforeEach(function () {
|
||||
// Mocked Data
|
||||
platformResourcesDir = PATH_RESOURCE;
|
||||
preparedIcons = {
|
||||
android_icons: {
|
||||
mdpi: mockGetIconItem({
|
||||
density: 'mdpi',
|
||||
background: 'res/icon/android/mdpi-background.png',
|
||||
foreground: 'res/icon/android/mdpi-foreground.png',
|
||||
monochrome: 'res/icon/android/mdpi-monochrome.png'
|
||||
})
|
||||
},
|
||||
default_icon: undefined
|
||||
};
|
||||
describe('without monochrome', () => {
|
||||
beforeEach(function () {
|
||||
// Mocked Data
|
||||
platformResourcesDir = PATH_RESOURCE;
|
||||
preparedIcons = {
|
||||
android_icons: {
|
||||
mdpi: mockGetIconItem({
|
||||
density: 'mdpi',
|
||||
background: 'res/icon/android/mdpi-background.png',
|
||||
foreground: 'res/icon/android/mdpi-foreground.png'
|
||||
})
|
||||
},
|
||||
default_icon: undefined
|
||||
};
|
||||
|
||||
resourceMap = createResourceMap();
|
||||
resourceMap = createResourceMap();
|
||||
|
||||
fsWriteFileSyncSpy = jasmine.createSpy('writeFileSync');
|
||||
prepare.__set__('fs', {
|
||||
writeFileSync: fsWriteFileSyncSpy
|
||||
fsWriteFileSyncSpy = jasmine.createSpy('writeFileSync');
|
||||
prepare.__set__('fs', {
|
||||
writeFileSync: fsWriteFileSyncSpy
|
||||
});
|
||||
});
|
||||
|
||||
it('Test#001 : Should update resource map with prepared icons.', function () {
|
||||
// Get method for testing
|
||||
const updateIconResourceForAdaptive = prepare.__get__('updateIconResourceForAdaptive');
|
||||
|
||||
// Run Test
|
||||
const expectedModification = {};
|
||||
expectedModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_background.png')] = 'res/icon/android/mdpi-background.png';
|
||||
expectedModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_foreground.png')] = 'res/icon/android/mdpi-foreground.png';
|
||||
|
||||
const expected = Object.assign({}, resourceMap, expectedModification);
|
||||
const actual = updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
it('Test#001 : Should update resource map with prepared icons.', function () {
|
||||
// Get method for testing
|
||||
const updateIconResourceForAdaptive = prepare.__get__('updateIconResourceForAdaptive');
|
||||
describe('with monochrome', () => {
|
||||
beforeEach(function () {
|
||||
// Mocked Data
|
||||
platformResourcesDir = PATH_RESOURCE;
|
||||
preparedIcons = {
|
||||
android_icons: {
|
||||
mdpi: mockGetIconItem({
|
||||
density: 'mdpi',
|
||||
background: 'res/icon/android/mdpi-background.png',
|
||||
foreground: 'res/icon/android/mdpi-foreground.png',
|
||||
monochrome: 'res/icon/android/mdpi-monochrome.png'
|
||||
})
|
||||
},
|
||||
default_icon: undefined
|
||||
};
|
||||
|
||||
// Run Test
|
||||
const expectedModification = {};
|
||||
expectedModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_background.png')] = 'res/icon/android/mdpi-background.png';
|
||||
expectedModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_foreground.png')] = 'res/icon/android/mdpi-foreground.png';
|
||||
expectedModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_monochrome.png')] = 'res/icon/android/mdpi-monochrome.png';
|
||||
resourceMap = createResourceMap();
|
||||
|
||||
const expected = Object.assign({}, resourceMap, expectedModification);
|
||||
const actual = updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
|
||||
fsWriteFileSyncSpy = jasmine.createSpy('writeFileSync');
|
||||
prepare.__set__('fs', {
|
||||
writeFileSync: fsWriteFileSyncSpy
|
||||
});
|
||||
});
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
it('Test#002 : Should update resource map with prepared icons.', function () {
|
||||
// Get method for testing
|
||||
const updateIconResourceForAdaptive = prepare.__get__('updateIconResourceForAdaptive');
|
||||
|
||||
// Run Test
|
||||
const expectedModification = {};
|
||||
expectedModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_background.png')] = 'res/icon/android/mdpi-background.png';
|
||||
expectedModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_foreground.png')] = 'res/icon/android/mdpi-foreground.png';
|
||||
expectedModification[path.join(PATH_RESOURCE, 'mipmap-mdpi-v26', 'ic_launcher_monochrome.png')] = 'res/icon/android/mdpi-monochrome.png';
|
||||
|
||||
const expected = Object.assign({}, resourceMap, expectedModification);
|
||||
const actual = updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('Test#003 : should emit if monochrome is supplied without adaptive background', () => {
|
||||
const updateIconResourceForAdaptive = prepare.__get__('updateIconResourceForAdaptive');
|
||||
|
||||
preparedIcons.android_icons.mdpi.background = undefined;
|
||||
updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
|
||||
|
||||
const actualEmitArgs = emitSpy.calls.mostRecent().args;
|
||||
expect(actualEmitArgs[0]).toBe('warn');
|
||||
expect(actualEmitArgs[1]).toMatch(/Monochrome icon found but without adaptive properties./g);
|
||||
});
|
||||
|
||||
it('Test#004 : should emit if monochrome is supplied without adaptive foreground', () => {
|
||||
const updateIconResourceForAdaptive = prepare.__get__('updateIconResourceForAdaptive');
|
||||
|
||||
preparedIcons.android_icons.mdpi.foreground = undefined;
|
||||
updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
|
||||
|
||||
const actualEmitArgs = emitSpy.calls.mostRecent().args;
|
||||
expect(actualEmitArgs[0]).toBe('warn');
|
||||
expect(actualEmitArgs[1]).toMatch(/Monochrome icon found but without adaptive properties./g);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -836,9 +899,9 @@ describe('prepare', () => {
|
||||
|
||||
// Spies
|
||||
let replaceFileContents;
|
||||
let ensureDirSyncSpy;
|
||||
let copySyncSpy;
|
||||
let removeSyncSpy;
|
||||
let mkdirSyncSpy;
|
||||
let cpSyncSpy;
|
||||
let rmSyncSpy;
|
||||
|
||||
// Mock Data
|
||||
let cordovaProject;
|
||||
@@ -867,7 +930,7 @@ describe('prepare', () => {
|
||||
locations: {
|
||||
plugins: '/mock/plugins',
|
||||
www: '/mock/www',
|
||||
strings: '/mock/res/values/strings.xml'
|
||||
strings: '/mock/res/values/cdv_strings.xml'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -887,7 +950,7 @@ describe('prepare', () => {
|
||||
|
||||
prepare.__set__('updateWww', jasmine.createSpy('updateWww'));
|
||||
prepare.__set__('updateIcons', jasmine.createSpy('updateIcons').and.returnValue(Promise.resolve()));
|
||||
prepare.__set__('updateProjectSplashScreen', jasmine.createSpy('updateProjectSplashScreen'));
|
||||
prepare.__set__('updateProjectTheme', jasmine.createSpy('updateProjectTheme'));
|
||||
prepare.__set__('warnForDeprecatedSplashScreen', jasmine.createSpy('warnForDeprecatedSplashScreen')
|
||||
.and.returnValue(Promise.resolve()));
|
||||
prepare.__set__('updateFileResources', jasmine.createSpy('updateFileResources').and.returnValue(Promise.resolve()));
|
||||
@@ -938,16 +1001,15 @@ describe('prepare', () => {
|
||||
`))
|
||||
});
|
||||
|
||||
ensureDirSyncSpy = jasmine.createSpy('ensureDirSync');
|
||||
copySyncSpy = jasmine.createSpy('copySync');
|
||||
removeSyncSpy = jasmine.createSpy('removeSync');
|
||||
mkdirSyncSpy = jasmine.createSpy('mkdirSync');
|
||||
cpSyncSpy = jasmine.createSpy('cpSync');
|
||||
rmSyncSpy = jasmine.createSpy('rmSync');
|
||||
|
||||
prepare.__set__('fs', {
|
||||
writeFileSync: jasmine.createSpy('writeFileSync'),
|
||||
writeJSONSync: jasmine.createSpy('writeJSONSync'),
|
||||
ensureDirSync: ensureDirSyncSpy,
|
||||
copySync: copySyncSpy,
|
||||
removeSync: removeSyncSpy,
|
||||
mkdirSync: mkdirSyncSpy,
|
||||
cpSync: cpSyncSpy,
|
||||
rmSync: rmSyncSpy,
|
||||
existsSync: jasmine.createSpy('existsSync')
|
||||
});
|
||||
});
|
||||
@@ -959,9 +1021,9 @@ describe('prepare', () => {
|
||||
|
||||
await api.prepare(cordovaProject, options).then(() => {
|
||||
expect(replaceFileContents).toHaveBeenCalledWith(renamedJavaActivityPath, /package [\w.]*;/, 'package ' + packageName + ';');
|
||||
expect(ensureDirSyncSpy).toHaveBeenCalledWith(renamedPath);
|
||||
expect(copySyncSpy).toHaveBeenCalledWith(initialJavaActivityPath, renamedJavaActivityPath);
|
||||
expect(removeSyncSpy).toHaveBeenCalledWith(initialJavaActivityPath);
|
||||
expect(mkdirSyncSpy).toHaveBeenCalledWith(renamedPath, { recursive: true });
|
||||
expect(cpSyncSpy).toHaveBeenCalledWith(initialJavaActivityPath, renamedJavaActivityPath);
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(initialJavaActivityPath);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -970,9 +1032,9 @@ describe('prepare', () => {
|
||||
|
||||
await api.prepare(cordovaProject, options).then(() => {
|
||||
expect(replaceFileContents).toHaveBeenCalledTimes(0);
|
||||
expect(ensureDirSyncSpy).toHaveBeenCalledTimes(0);
|
||||
expect(copySyncSpy).toHaveBeenCalledTimes(0);
|
||||
expect(removeSyncSpy).toHaveBeenCalledTimes(0);
|
||||
expect(mkdirSyncSpy).toHaveBeenCalledTimes(0);
|
||||
expect(cpSyncSpy).toHaveBeenCalledTimes(0);
|
||||
expect(rmSyncSpy).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
<?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
|
||||
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
|
||||
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.
|
||||
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.
|
||||
-->
|
||||
<widget xmlns = "http://www.w3.org/ns/widgets"
|
||||
id = "io.cordova.helloCordova"
|
||||
version = "2.0.0">
|
||||
<widget xmlns="http://www.w3.org/ns/widgets"
|
||||
xmlns:cdv="http://cordova.apache.org/ns/1.0"
|
||||
id="org.apache.cordova.hellocordova"
|
||||
version="1.0.0">
|
||||
|
||||
<!-- Preferences for Android -->
|
||||
<preference name="loglevel" value="DEBUG" />
|
||||
|
||||
@@ -39,12 +39,21 @@
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/Theme.App.SplashScreen"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation"
|
||||
android:exported="true">
|
||||
<intent-filter android:label="@string/launcher_name">
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<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,7 +183,11 @@ task cdvPrintProps {
|
||||
}
|
||||
|
||||
android {
|
||||
namespace cordovaConfig.PACKAGE_NAMESPACE
|
||||
namespace = cordovaConfig.PACKAGE_NAMESPACE
|
||||
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
versionCode cdvVersionCode ?: new BigInteger("" + privateHelpers.extractIntFromManifest("versionCode"))
|
||||
@@ -194,7 +202,7 @@ android {
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
abortOnError = false
|
||||
}
|
||||
|
||||
buildToolsVersion cordovaConfig.BUILD_TOOLS_VERSION
|
||||
@@ -248,8 +256,19 @@ 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 KOTLIN_JVM_TARGET is null, fallback to JAVA_TARGET_COMPATIBILITY,
|
||||
// as they generally should be equal
|
||||
cordovaConfig.KOTLIN_JVM_TARGET = cordovaConfig.KOTLIN_JVM_TARGET ?:
|
||||
cordovaConfig.JAVA_TARGET_COMPATIBILITY
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaLanguageVersion.of(cordovaConfig.KOTLIN_JVM_TARGET)
|
||||
}
|
||||
}
|
||||
|
||||
if (cdvReleaseSigningPropertiesFile) {
|
||||
@@ -292,7 +311,16 @@ dependencies {
|
||||
implementation "androidx.core:core-splashscreen:${cordovaConfig.ANDROIDX_CORE_SPLASHSCREEN_VERSION}"
|
||||
|
||||
if (cordovaConfig.IS_GRADLE_PLUGIN_KOTLIN_ENABLED) {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${cordovaConfig.KOTLIN_VERSION}"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:${cordovaConfig.KOTLIN_VERSION}"
|
||||
}
|
||||
|
||||
constraints {
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:${cordovaConfig.KOTLIN_VERSION}") {
|
||||
because("kotlin-stdlib-jdk7 is now a part of kotlin-stdlib")
|
||||
}
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${cordovaConfig.KOTLIN_VERSION}") {
|
||||
because("kotlin-stdlib-jdk8 is now a part of kotlin-stdlib")
|
||||
}
|
||||
}
|
||||
|
||||
// SUB-PROJECT DEPENDENCIES START
|
||||
|
||||
@@ -40,6 +40,18 @@ allprojects {
|
||||
}
|
||||
|
||||
repositories repos
|
||||
|
||||
subprojects {
|
||||
afterEvaluate {
|
||||
tasks.withType(JavaCompile).tap {
|
||||
configureEach {
|
||||
if (cordovaConfig.JAVA_SHOW_DEPRECATIONS == true) {
|
||||
options.compilerArgs += "-Xlint:deprecation"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
|
||||
@@ -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,4 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<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" />
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<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" />
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<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" />
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<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" />
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<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" />
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user