diff --git a/.eslintignore b/.eslintignore
index 0d61c6cc..811a23b6 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,2 +1,3 @@
bin/templates/project/assets/www/cordova.js
-test/app
+test/android/app
+test/androidx/app
diff --git a/.gitignore b/.gitignore
index a1b3317a..4c093a9b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,9 +26,14 @@ example
/framework/javadoc-public
/framework/javadoc-private
/test/.externalNativeBuild
-/test/gradle
-/test/gradlew
-/test/gradlew.bat
+
+/test/android/gradle
+/test/android/gradlew
+/test/android/gradlew.bat
+/test/androidx/gradle
+/test/androidx/gradlew
+/test/androidx/gradlew.bat
+
/test/assets/www/.tmp*
/test/assets/www/cordova.js
/test/bin
diff --git a/bin/templates/cordova/lib/config/GradlePropertiesParser.js b/bin/templates/cordova/lib/config/GradlePropertiesParser.js
index 060da2d9..53a96386 100644
--- a/bin/templates/cordova/lib/config/GradlePropertiesParser.js
+++ b/bin/templates/cordova/lib/config/GradlePropertiesParser.js
@@ -34,7 +34,11 @@ class GradlePropertiesParser {
'org.gradle.daemon': 'true',
// to allow dex in process
- 'org.gradle.jvmargs': '-Xmx2048m'
+ 'org.gradle.jvmargs': '-Xmx2048m',
+
+ // Android X
+ 'android.useAndroidX': 'true',
+ 'android.enableJetifier': 'true'
// Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
// 'org.gradle.parallel': 'true'
diff --git a/bin/templates/cordova/lib/prepare.js b/bin/templates/cordova/lib/prepare.js
index e50278cd..a8d120d4 100644
--- a/bin/templates/cordova/lib/prepare.js
+++ b/bin/templates/cordova/lib/prepare.js
@@ -45,12 +45,19 @@ module.exports.prepare = function (cordovaProject, options) {
const minSdkVersion = this._config.getPreference('android-minSdkVersion', 'android');
const maxSdkVersion = this._config.getPreference('android-maxSdkVersion', 'android');
const targetSdkVersion = this._config.getPreference('android-targetSdkVersion', 'android');
+ const androidXEnabled = this._config.getPreference('AndroidXEnabled', 'android');
let gradlePropertiesUserConfig = {};
if (minSdkVersion) gradlePropertiesUserConfig.cdvMinSdkVersion = minSdkVersion;
if (maxSdkVersion) gradlePropertiesUserConfig.cdvMaxSdkVersion = maxSdkVersion;
if (targetSdkVersion) gradlePropertiesUserConfig.cdvTargetSdkVersion = targetSdkVersion;
+ // Both 'useAndroidX' and 'enableJetifier' are linked together.
+ if (androidXEnabled) {
+ gradlePropertiesUserConfig['android.useAndroidX'] = androidXEnabled;
+ gradlePropertiesUserConfig['android.enableJetifier'] = androidXEnabled;
+ }
+
let gradlePropertiesParser = new GradlePropertiesParser(this.locations.root);
gradlePropertiesParser.configure(gradlePropertiesUserConfig);
diff --git a/test/README.md b/test/android/README.md
similarity index 100%
rename from test/README.md
rename to test/android/README.md
diff --git a/test/app/.gitignore b/test/android/app/.gitignore
similarity index 100%
rename from test/app/.gitignore
rename to test/android/app/.gitignore
diff --git a/test/app/build.gradle b/test/android/app/build.gradle
similarity index 100%
rename from test/app/build.gradle
rename to test/android/app/build.gradle
diff --git a/test/app/proguard-rules.pro b/test/android/app/proguard-rules.pro
similarity index 100%
rename from test/app/proguard-rules.pro
rename to test/android/app/proguard-rules.pro
diff --git a/test/app/src/androidTest/java/org/apache/cordova/unittests/BackButtonMultipageTest.java b/test/android/app/src/androidTest/java/org/apache/cordova/unittests/BackButtonMultipageTest.java
similarity index 100%
rename from test/app/src/androidTest/java/org/apache/cordova/unittests/BackButtonMultipageTest.java
rename to test/android/app/src/androidTest/java/org/apache/cordova/unittests/BackButtonMultipageTest.java
diff --git a/test/app/src/androidTest/java/org/apache/cordova/unittests/EmbeddedWebViewTest.java b/test/android/app/src/androidTest/java/org/apache/cordova/unittests/EmbeddedWebViewTest.java
similarity index 100%
rename from test/app/src/androidTest/java/org/apache/cordova/unittests/EmbeddedWebViewTest.java
rename to test/android/app/src/androidTest/java/org/apache/cordova/unittests/EmbeddedWebViewTest.java
diff --git a/test/app/src/androidTest/java/org/apache/cordova/unittests/ErrorUrlTest.java b/test/android/app/src/androidTest/java/org/apache/cordova/unittests/ErrorUrlTest.java
similarity index 100%
rename from test/app/src/androidTest/java/org/apache/cordova/unittests/ErrorUrlTest.java
rename to test/android/app/src/androidTest/java/org/apache/cordova/unittests/ErrorUrlTest.java
diff --git a/test/app/src/androidTest/java/org/apache/cordova/unittests/IFrameTest.java b/test/android/app/src/androidTest/java/org/apache/cordova/unittests/IFrameTest.java
similarity index 100%
rename from test/app/src/androidTest/java/org/apache/cordova/unittests/IFrameTest.java
rename to test/android/app/src/androidTest/java/org/apache/cordova/unittests/IFrameTest.java
diff --git a/test/app/src/androidTest/java/org/apache/cordova/unittests/MessageChannelMultipageTest.java b/test/android/app/src/androidTest/java/org/apache/cordova/unittests/MessageChannelMultipageTest.java
similarity index 100%
rename from test/app/src/androidTest/java/org/apache/cordova/unittests/MessageChannelMultipageTest.java
rename to test/android/app/src/androidTest/java/org/apache/cordova/unittests/MessageChannelMultipageTest.java
diff --git a/test/app/src/androidTest/java/org/apache/cordova/unittests/StandardActivityTest.java b/test/android/app/src/androidTest/java/org/apache/cordova/unittests/StandardActivityTest.java
similarity index 100%
rename from test/app/src/androidTest/java/org/apache/cordova/unittests/StandardActivityTest.java
rename to test/android/app/src/androidTest/java/org/apache/cordova/unittests/StandardActivityTest.java
diff --git a/test/app/src/main/AndroidManifest.xml b/test/android/app/src/main/AndroidManifest.xml
similarity index 100%
rename from test/app/src/main/AndroidManifest.xml
rename to test/android/app/src/main/AndroidManifest.xml
diff --git a/test/app/src/main/assets/www/backbuttonmultipage/index.html b/test/android/app/src/main/assets/www/backbuttonmultipage/index.html
similarity index 100%
rename from test/app/src/main/assets/www/backbuttonmultipage/index.html
rename to test/android/app/src/main/assets/www/backbuttonmultipage/index.html
diff --git a/test/app/src/main/assets/www/backbuttonmultipage/sample2.html b/test/android/app/src/main/assets/www/backbuttonmultipage/sample2.html
similarity index 100%
rename from test/app/src/main/assets/www/backbuttonmultipage/sample2.html
rename to test/android/app/src/main/assets/www/backbuttonmultipage/sample2.html
diff --git a/test/app/src/main/assets/www/backbuttonmultipage/sample3.html b/test/android/app/src/main/assets/www/backbuttonmultipage/sample3.html
similarity index 100%
rename from test/app/src/main/assets/www/backbuttonmultipage/sample3.html
rename to test/android/app/src/main/assets/www/backbuttonmultipage/sample3.html
diff --git a/test/app/src/main/assets/www/backgroundcolor/index.html b/test/android/app/src/main/assets/www/backgroundcolor/index.html
similarity index 100%
rename from test/app/src/main/assets/www/backgroundcolor/index.html
rename to test/android/app/src/main/assets/www/backgroundcolor/index.html
diff --git a/test/app/src/main/assets/www/cordova.js b/test/android/app/src/main/assets/www/cordova.js
similarity index 100%
rename from test/app/src/main/assets/www/cordova.js
rename to test/android/app/src/main/assets/www/cordova.js
diff --git a/test/app/src/main/assets/www/cordova_plugins.js b/test/android/app/src/main/assets/www/cordova_plugins.js
similarity index 100%
rename from test/app/src/main/assets/www/cordova_plugins.js
rename to test/android/app/src/main/assets/www/cordova_plugins.js
diff --git a/test/app/src/main/assets/www/fullscreen/index.html b/test/android/app/src/main/assets/www/fullscreen/index.html
similarity index 100%
rename from test/app/src/main/assets/www/fullscreen/index.html
rename to test/android/app/src/main/assets/www/fullscreen/index.html
diff --git a/test/app/src/main/assets/www/htmlnotfound/error.html b/test/android/app/src/main/assets/www/htmlnotfound/error.html
similarity index 100%
rename from test/app/src/main/assets/www/htmlnotfound/error.html
rename to test/android/app/src/main/assets/www/htmlnotfound/error.html
diff --git a/test/app/src/main/assets/www/iframe/index.html b/test/android/app/src/main/assets/www/iframe/index.html
similarity index 100%
rename from test/app/src/main/assets/www/iframe/index.html
rename to test/android/app/src/main/assets/www/iframe/index.html
diff --git a/test/app/src/main/assets/www/iframe/index2.html b/test/android/app/src/main/assets/www/iframe/index2.html
similarity index 100%
rename from test/app/src/main/assets/www/iframe/index2.html
rename to test/android/app/src/main/assets/www/iframe/index2.html
diff --git a/test/app/src/main/assets/www/index.html b/test/android/app/src/main/assets/www/index.html
similarity index 100%
rename from test/app/src/main/assets/www/index.html
rename to test/android/app/src/main/assets/www/index.html
diff --git a/test/app/src/main/assets/www/lifecycle/index.html b/test/android/app/src/main/assets/www/lifecycle/index.html
similarity index 100%
rename from test/app/src/main/assets/www/lifecycle/index.html
rename to test/android/app/src/main/assets/www/lifecycle/index.html
diff --git a/test/app/src/main/assets/www/lifecycle/index2.html b/test/android/app/src/main/assets/www/lifecycle/index2.html
similarity index 100%
rename from test/app/src/main/assets/www/lifecycle/index2.html
rename to test/android/app/src/main/assets/www/lifecycle/index2.html
diff --git a/test/app/src/main/assets/www/main.js b/test/android/app/src/main/assets/www/main.js
similarity index 100%
rename from test/app/src/main/assets/www/main.js
rename to test/android/app/src/main/assets/www/main.js
diff --git a/test/app/src/main/assets/www/master.css b/test/android/app/src/main/assets/www/master.css
similarity index 100%
rename from test/app/src/main/assets/www/master.css
rename to test/android/app/src/main/assets/www/master.css
diff --git a/test/app/src/main/assets/www/whitelist/index.html b/test/android/app/src/main/assets/www/whitelist/index.html
similarity index 100%
rename from test/app/src/main/assets/www/whitelist/index.html
rename to test/android/app/src/main/assets/www/whitelist/index.html
diff --git a/test/app/src/main/assets/www/whitelist/index2.html b/test/android/app/src/main/assets/www/whitelist/index2.html
similarity index 100%
rename from test/app/src/main/assets/www/whitelist/index2.html
rename to test/android/app/src/main/assets/www/whitelist/index2.html
diff --git a/test/app/src/main/java/org/apache/cordova/unittests/EmbeddedWebViewActivity.java b/test/android/app/src/main/java/org/apache/cordova/unittests/EmbeddedWebViewActivity.java
similarity index 100%
rename from test/app/src/main/java/org/apache/cordova/unittests/EmbeddedWebViewActivity.java
rename to test/android/app/src/main/java/org/apache/cordova/unittests/EmbeddedWebViewActivity.java
diff --git a/test/app/src/main/java/org/apache/cordova/unittests/LifeCyclePlugin.java b/test/android/app/src/main/java/org/apache/cordova/unittests/LifeCyclePlugin.java
similarity index 100%
rename from test/app/src/main/java/org/apache/cordova/unittests/LifeCyclePlugin.java
rename to test/android/app/src/main/java/org/apache/cordova/unittests/LifeCyclePlugin.java
diff --git a/test/app/src/main/java/org/apache/cordova/unittests/StandardActivity.java b/test/android/app/src/main/java/org/apache/cordova/unittests/StandardActivity.java
similarity index 100%
rename from test/app/src/main/java/org/apache/cordova/unittests/StandardActivity.java
rename to test/android/app/src/main/java/org/apache/cordova/unittests/StandardActivity.java
diff --git a/test/app/src/main/java/org/apache/cordova/unittests/TestActivity.java b/test/android/app/src/main/java/org/apache/cordova/unittests/TestActivity.java
similarity index 100%
rename from test/app/src/main/java/org/apache/cordova/unittests/TestActivity.java
rename to test/android/app/src/main/java/org/apache/cordova/unittests/TestActivity.java
diff --git a/test/app/src/main/res/layout/activity_main.xml b/test/android/app/src/main/res/layout/activity_main.xml
similarity index 100%
rename from test/app/src/main/res/layout/activity_main.xml
rename to test/android/app/src/main/res/layout/activity_main.xml
diff --git a/test/app/src/main/res/mipmap-hdpi/ic_launcher.png b/test/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
similarity index 100%
rename from test/app/src/main/res/mipmap-hdpi/ic_launcher.png
rename to test/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
diff --git a/test/app/src/main/res/mipmap-mdpi/ic_launcher.png b/test/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
similarity index 100%
rename from test/app/src/main/res/mipmap-mdpi/ic_launcher.png
rename to test/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
diff --git a/test/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/test/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
similarity index 100%
rename from test/app/src/main/res/mipmap-xhdpi/ic_launcher.png
rename to test/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
diff --git a/test/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/test/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
similarity index 100%
rename from test/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
rename to test/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
diff --git a/test/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/test/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
similarity index 100%
rename from test/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
rename to test/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
diff --git a/test/app/src/main/res/values-w820dp/dimens.xml b/test/android/app/src/main/res/values-w820dp/dimens.xml
similarity index 100%
rename from test/app/src/main/res/values-w820dp/dimens.xml
rename to test/android/app/src/main/res/values-w820dp/dimens.xml
diff --git a/test/app/src/main/res/values/colors.xml b/test/android/app/src/main/res/values/colors.xml
similarity index 100%
rename from test/app/src/main/res/values/colors.xml
rename to test/android/app/src/main/res/values/colors.xml
diff --git a/test/app/src/main/res/values/dimens.xml b/test/android/app/src/main/res/values/dimens.xml
similarity index 100%
rename from test/app/src/main/res/values/dimens.xml
rename to test/android/app/src/main/res/values/dimens.xml
diff --git a/test/app/src/main/res/values/strings.xml b/test/android/app/src/main/res/values/strings.xml
similarity index 100%
rename from test/app/src/main/res/values/strings.xml
rename to test/android/app/src/main/res/values/strings.xml
diff --git a/test/app/src/main/res/values/styles.xml b/test/android/app/src/main/res/values/styles.xml
similarity index 100%
rename from test/app/src/main/res/values/styles.xml
rename to test/android/app/src/main/res/values/styles.xml
diff --git a/test/app/src/main/res/xml/config.xml b/test/android/app/src/main/res/xml/config.xml
similarity index 100%
rename from test/app/src/main/res/xml/config.xml
rename to test/android/app/src/main/res/xml/config.xml
diff --git a/test/app/src/test/java/org/apache/cordova/unittests/NativeToJsMessageQueueTest.java b/test/android/app/src/test/java/org/apache/cordova/unittests/NativeToJsMessageQueueTest.java
similarity index 100%
rename from test/app/src/test/java/org/apache/cordova/unittests/NativeToJsMessageQueueTest.java
rename to test/android/app/src/test/java/org/apache/cordova/unittests/NativeToJsMessageQueueTest.java
diff --git a/test/build.gradle b/test/android/build.gradle
similarity index 100%
rename from test/build.gradle
rename to test/android/build.gradle
diff --git a/test/gradle.properties b/test/android/gradle.properties
similarity index 100%
rename from test/gradle.properties
rename to test/android/gradle.properties
diff --git a/test/settings.gradle b/test/android/settings.gradle
similarity index 92%
rename from test/settings.gradle
rename to test/android/settings.gradle
index 52360220..ac3d9ebd 100644
--- a/test/settings.gradle
+++ b/test/android/settings.gradle
@@ -18,4 +18,4 @@
include ':app'
include ":CordovaLib"
-project(':CordovaLib').projectDir = new File('../framework')
+project(':CordovaLib').projectDir = new File('../../framework')
diff --git a/test/wrapper.gradle b/test/android/wrapper.gradle
similarity index 100%
rename from test/wrapper.gradle
rename to test/android/wrapper.gradle
diff --git a/test/androidx/README.md b/test/androidx/README.md
new file mode 100644
index 00000000..a6c62f55
--- /dev/null
+++ b/test/androidx/README.md
@@ -0,0 +1,60 @@
+
+
+# Cordova Android Test Project
+
+The project in this directory is an Android Test project that enables those
+interested in further developing `cordova-android` to validate their changes.
+
+## Requirements
+
+The requirements in the [top-level README](../README.md) still apply. In
+addition, ensure you have installed Gradle, and that it is (at the time of this
+writing) at least version 3.3 or newer.
+
+## Getting Started
+
+You can run this test project from both the command line as well as from
+Android Studio:
+
+### Command Line
+
+Ensure you have the gradle wrapper script, `gradlew`, in this directory. If
+you do not, you can run the following to generate it:
+
+ $ cd cordova-android/test
+ $ gradle :wrapper -b build.gradle
+
+You can then see a list of all tasks available to run with `gradlew tasks`.
+
+The two different kinds of tests one typically wants to run are unit tests and
+end-to-end, or instrumented, tests. Unit tests do not require any particular
+environment to run in, but the instrumented tests, however, require a connected
+Android device or emulator to run in.
+
+- To run the unit tests, run: `gradlew test`.
+- To run the instrumented tests, run: `gradlew connectedAndroidTest`.
+
+To make sure all tests are run, add the `--rerun-tasks` parameter.
+
+### Android Studio
+
+Import this `test/` directory into Android Studio, and hit the Play button.
diff --git a/test/androidx/app/.gitignore b/test/androidx/app/.gitignore
new file mode 100644
index 00000000..796b96d1
--- /dev/null
+++ b/test/androidx/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/test/androidx/app/build.gradle b/test/androidx/app/build.gradle
new file mode 100644
index 00000000..08642127
--- /dev/null
+++ b/test/androidx/app/build.gradle
@@ -0,0 +1,57 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 29
+ buildToolsVersion "29.0.2"
+
+ defaultConfig {
+ applicationId "org.apache.cordova.unittests"
+ minSdkVersion 19
+ targetSdkVersion 29
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation project(path: ':CordovaLib')
+ implementation 'androidx.appcompat:appcompat:1.0.2'
+
+ testImplementation 'org.json:json:20140107'
+ testImplementation 'junit:junit:4.12'
+
+ androidTestImplementation('androidx.test.espresso:espresso-core:3.1.1', {
+ exclude group: 'androidx.test.espresso', module: 'androidx.annotation'
+ })
+
+ androidTestImplementation('androidx.test.espresso:espresso-web:3.1.1', {
+ exclude group: 'androidx.test.espresso', module: 'androidx.annotation'
+ })
+}
diff --git a/test/androidx/app/proguard-rules.pro b/test/androidx/app/proguard-rules.pro
new file mode 100644
index 00000000..fa4b7eba
--- /dev/null
+++ b/test/androidx/app/proguard-rules.pro
@@ -0,0 +1,36 @@
+#
+# 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.
+#
+
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/jbowser/Library/Android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/BackButtonMultipageTest.java b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/BackButtonMultipageTest.java
new file mode 100644
index 00000000..5118c262
--- /dev/null
+++ b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/BackButtonMultipageTest.java
@@ -0,0 +1,158 @@
+/*
+ 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.unittests;
+
+import android.content.Intent;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.apache.cordova.CordovaWebView;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.pressBack;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.web.sugar.Web.onWebView;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+
+import static org.apache.cordova.unittests.R.id.cordovaWebView;
+
+
+@RunWith(AndroidJUnit4.class)
+public class BackButtonMultipageTest {
+
+ private static final String START_URL = "file:///android_asset/www/backbuttonmultipage/index.html";
+ //I have no idea why we picked 100, but we did.
+ private static final int WEBVIEW_ID = 100;
+ private TestActivity mActivity;
+
+ // Don't launch the activity, we're going to send it intents
+ @Rule
+ public ActivityTestRule mActivityRule = new ActivityTestRule<>(
+ TestActivity.class, true, false);
+
+ @Before
+ public void launchApplicationWithIntent() {
+ Intent intent = new Intent();
+ intent.putExtra("startUrl", START_URL);
+ mActivity = (TestActivity) mActivityRule.launchActivity(intent);
+ }
+
+ @Test
+ public void testViaHref() throws Throwable {
+ final CordovaWebView webInterface = mActivity.getWebInterface();
+ assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
+
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run() {
+ webInterface.sendJavascript("window.location = 'sample2.html';");
+ }
+ });
+ assertEquals("file:///android_asset/www/backbuttonmultipage/sample2.html", mActivity.onPageFinishedUrl.take());
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run() {
+ webInterface.sendJavascript("window.location = 'sample3.html';");
+ }
+ });
+ assertEquals("file:///android_asset/www/backbuttonmultipage/sample3.html", mActivity.onPageFinishedUrl.take());
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run() {
+ assertTrue(webInterface.backHistory());
+ }
+ });
+ assertEquals("file:///android_asset/www/backbuttonmultipage/sample2.html", mActivity.onPageFinishedUrl.take());
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run() {
+ assertTrue(webInterface.backHistory());
+ }
+ });
+ assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run() {
+ assertFalse(webInterface.backHistory());
+ }
+ });
+ }
+
+ @Test
+ public void testViaLoadUrl() throws Throwable {
+ final CordovaWebView webInterface = mActivity.getWebInterface();
+ assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
+
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run() {
+ webInterface.loadUrl("file:///android_asset/www/backbuttonmultipage/sample2.html");
+ }
+ });
+ assertEquals("file:///android_asset/www/backbuttonmultipage/sample2.html", mActivity.onPageFinishedUrl.take());
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run() {
+ webInterface.loadUrl("file:///android_asset/www/backbuttonmultipage/sample3.html");
+ }
+ });
+ assertEquals("file:///android_asset/www/backbuttonmultipage/sample3.html", mActivity.onPageFinishedUrl.take());
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run() {
+ assertTrue(webInterface.backHistory());
+ }
+ });
+ assertEquals("file:///android_asset/www/backbuttonmultipage/sample2.html", mActivity.onPageFinishedUrl.take());
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run() {
+ assertTrue(webInterface.backHistory());
+ }
+ });
+ assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run() {
+ assertFalse(webInterface.backHistory());
+ }
+ });
+ }
+
+ @Test
+ public void testViaBackButtonOnView() throws Throwable {
+ final CordovaWebView webInterface = mActivity.getWebInterface();
+ assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
+
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run() {
+ webInterface.loadUrl("file:///android_asset/www/backbuttonmultipage/sample2.html");
+ }
+ });
+ assertEquals("file:///android_asset/www/backbuttonmultipage/sample2.html", mActivity.onPageFinishedUrl.take());
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run() {
+ webInterface.loadUrl("file:///android_asset/www/backbuttonmultipage/sample3.html");
+ }
+ });
+ assertEquals("file:///android_asset/www/backbuttonmultipage/sample3.html", mActivity.onPageFinishedUrl.take());
+ onView(withId(WEBVIEW_ID)).perform(pressBack());
+ assertEquals("file:///android_asset/www/backbuttonmultipage/sample2.html", mActivity.onPageFinishedUrl.take());
+ onView(withId(WEBVIEW_ID)).perform(pressBack());
+ assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
+ }
+}
diff --git a/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/EmbeddedWebViewTest.java b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/EmbeddedWebViewTest.java
new file mode 100644
index 00000000..03c01619
--- /dev/null
+++ b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/EmbeddedWebViewTest.java
@@ -0,0 +1,53 @@
+/*
+ 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.unittests;
+
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static junit.framework.Assert.assertNotNull;
+
+/*
+ * This test is to cover the use case of Cordova Android used as a component in a larger Android
+ * application. This test is strictly used to cover this use case. In this example, the WebView
+ * should load, not be null, and the Plugin Manager should also be initialized.
+ *
+
+ */
+
+
+@RunWith(AndroidJUnit4.class)
+public class EmbeddedWebViewTest {
+
+ @Rule
+ public ActivityTestRule mActivityRule = new ActivityTestRule<>(
+ EmbeddedWebViewActivity.class);
+
+ @Test
+ public void checkWebViewTest() {
+ EmbeddedWebViewActivity activity = (EmbeddedWebViewActivity) mActivityRule.getActivity();
+ assertNotNull(activity.webInterface);
+ assertNotNull(activity.webInterface.getPluginManager());
+ }
+}
diff --git a/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/ErrorUrlTest.java b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/ErrorUrlTest.java
new file mode 100644
index 00000000..e1d160e3
--- /dev/null
+++ b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/ErrorUrlTest.java
@@ -0,0 +1,68 @@
+/*
+ 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.unittests;
+
+import android.content.Intent;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.apache.cordova.CordovaWebView;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static junit.framework.Assert.assertEquals;
+
+@RunWith(AndroidJUnit4.class)
+public class ErrorUrlTest {
+ private static final String START_URL = "file:///android_asset/www/htmlnotfound/index.html";
+ private static final String ERROR_URL = "file:///android_asset/www/htmlnotfound/error.html";
+ private static final String INVALID_URL = "file:///android_asset/www/invalid.html";
+
+ //I have no idea why we picked 100, but we did.
+ private static final int WEBVIEW_ID = 100;
+ private TestActivity mActivity;
+
+ @Rule
+ public ActivityTestRule mActivityRule = new ActivityTestRule<>(
+ TestActivity.class);
+
+ @Before
+ public void launchApplicationWithIntent() {
+ Intent intent = new Intent();
+ intent.putExtra("startUrl", START_URL);
+ intent.putExtra("errorurl", INVALID_URL);
+ intent.putExtra("url", INVALID_URL);
+ mActivity = (TestActivity) mActivityRule.launchActivity(intent);
+ }
+
+ @Test
+ public void errorUrlTest() throws Throwable {
+ assertEquals(START_URL, mActivity.onPageFinishedUrl.take());
+ assertEquals(ERROR_URL, mActivity.onPageFinishedUrl.take());
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run() {
+ assertEquals(ERROR_URL, mActivity.getWebInterface().getUrl());
+ }
+ });
+ }
+}
diff --git a/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/IFrameTest.java b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/IFrameTest.java
new file mode 100644
index 00000000..3e5d66e4
--- /dev/null
+++ b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/IFrameTest.java
@@ -0,0 +1,114 @@
+/*
+ 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.unittests;
+
+import android.content.Intent;
+import androidx.test.espresso.web.webdriver.Locator;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.apache.cordova.CordovaWebView;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.pressBack;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.web.sugar.Web.onWebView;
+import static androidx.test.espresso.web.webdriver.DriverAtoms.findElement;
+import static androidx.test.espresso.web.webdriver.DriverAtoms.webClick;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+@RunWith(AndroidJUnit4.class)
+public class IFrameTest {
+
+ private static final String START_URL = "file:///android_asset/www/iframe/index.html";
+ //I have no idea why we picked 100, but we did.
+ private static final int WEBVIEW_ID = 100;
+ private int WEBVIEW_LOAD_DELAY = 500;
+
+ private TestActivity testActivity;
+
+ // Don't launch the activity, we're going to send it intents
+ @Rule
+ public ActivityTestRule mActivityRule = new ActivityTestRule<>(
+ TestActivity.class, true, false);
+
+ @Before
+ public void launchApplicationWithIntent() {
+ Intent intent = new Intent();
+ intent.putExtra("startUrl", START_URL);
+ testActivity = (TestActivity) mActivityRule.launchActivity(intent);
+ }
+
+ @Test
+ public void iFrameHistory() throws Throwable {
+ final CordovaWebView cordovaWebView = (CordovaWebView) testActivity.getWebInterface();
+ onWebView().withElement(findElement(Locator.ID, "google_maps")).perform(webClick());
+ sleep(WEBVIEW_LOAD_DELAY);
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run()
+ {
+ String url = cordovaWebView.getUrl();
+ assertTrue(url.endsWith("index.html"));
+ }
+ });
+ sleep(WEBVIEW_LOAD_DELAY);
+ onWebView().withElement(findElement(Locator.ID, "javascript_load")).perform(webClick());
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run()
+ {
+ String url = cordovaWebView.getUrl();
+ assertTrue(url.endsWith("index.html"));
+ }
+ });
+ sleep(WEBVIEW_LOAD_DELAY);
+ //Espresso will kill the application and not trigger the backHistory method, which correctly
+ //navigates the iFrame history. backHistory is tied to the back button.
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run()
+ {
+ assertTrue(cordovaWebView.backHistory());
+ String url = cordovaWebView.getUrl();
+ assertTrue(url.endsWith("index.html"));
+ assertFalse(cordovaWebView.backHistory());
+ }
+ });
+
+ }
+
+
+ //BRUTE FORCE THE CRAP OUT OF CONCURRENCY ERRORS
+ private void sleep(int timeout) {
+ try {
+ Thread.sleep(timeout);
+ } catch (InterruptedException e) {
+ fail("Unexpected Timeout");
+ }
+ }
+
+}
diff --git a/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/MessageChannelMultipageTest.java b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/MessageChannelMultipageTest.java
new file mode 100644
index 00000000..2958addd
--- /dev/null
+++ b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/MessageChannelMultipageTest.java
@@ -0,0 +1,130 @@
+/*
+ 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.unittests;
+
+import android.content.Intent;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.apache.cordova.CallbackContext;
+import org.apache.cordova.CordovaWebView;
+import org.apache.cordova.CordovaWebViewImpl;
+import org.apache.cordova.PluginManager;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.fail;
+
+@RunWith(AndroidJUnit4.class)
+public class MessageChannelMultipageTest {
+ private static final String START_URL = "file:///android_asset/www/backbuttonmultipage/index.html";
+ //I have no idea why we picked 100, but we did.
+ private static final int WEBVIEW_ID = 100;
+ private TestActivity testActivity;
+
+ @Rule
+ public ActivityTestRule mActivityRule = new ActivityTestRule<>(
+ TestActivity.class);
+
+ @Before
+ public void launchApplicationWithIntent() {
+ Intent intent = new Intent();
+ intent.putExtra("startUrl", START_URL);
+ testActivity = (TestActivity) mActivityRule.launchActivity(intent);
+ }
+
+
+
+ //test that after a page load the cached callback id and the live callback id match
+ //this is to prevent a regression
+ //the issue was that CordovaWebViewImpl's cached instance of CoreAndroid would become stale on page load
+ //this is because the cached instance was not being cleared when the pluginManager was reset on page load
+ //the plugin manager would get a new instance which would be updated with a new callback id
+ //the cached instance's message channel callback id would become stale
+ //effectively this caused message channel events to not be delivered
+ @Test
+ public void testThatCachedCallbackIdIsValid() throws Throwable {
+ final CordovaWebView cordovaWebView = testActivity.getWebInterface();
+ Class cordovaWebViewImpl = CordovaWebViewImpl.class;
+ //send a test event - this initializes cordovaWebViewImpl.appPlugin (the cached instance of CoreAndroid)
+ Method method = cordovaWebViewImpl.getDeclaredMethod("sendJavascriptEvent", String.class);
+ method.setAccessible(true);
+ method.invoke(cordovaWebView, "testEvent");
+ sleep(1000);
+
+ //load a page - this resets the plugin manager and nulls cordovaWebViewImpl.appPlugin
+ //(previously this resets plugin manager but did not null cordovaWebViewImpl.appPlugin, leading to the issue)
+ mActivityRule.runOnUiThread(new Runnable() {
+ public void run() {
+ cordovaWebView.loadUrl(START_URL);
+ }
+ });
+ assertEquals(START_URL, testActivity.onPageFinishedUrl.take());
+
+ //send a test event - this initializes cordovaWebViewImpl.appPlugin (the cached instance of CoreAndroid)
+ method.invoke(cordovaWebView, "testEvent");
+ sleep(1000);
+
+ //get reference to package protected class CoreAndroid
+ Class coreAndroid = Class.forName("org.apache.cordova.CoreAndroid");
+
+ //get cached CoreAndroid
+ Field appPluginField = cordovaWebViewImpl.getDeclaredField("appPlugin");
+ appPluginField.setAccessible(true);
+ Object cachedAppPlugin = appPluginField.get(cordovaWebView);
+ //get cached CallbackContext
+ Field messageChannelField = coreAndroid.getDeclaredField("messageChannel");
+ messageChannelField.setAccessible(true);
+ CallbackContext cachedCallbackContext = (CallbackContext) messageChannelField.get(cachedAppPlugin);
+
+ //get live CoreAndroid
+ PluginManager pluginManager = cordovaWebView.getPluginManager();
+ Field coreAndroidPluginNameField = coreAndroid.getField("PLUGIN_NAME");
+ String coreAndroidPluginName = (String) coreAndroidPluginNameField.get(null);
+ Object liveAppPlugin = pluginManager.getPlugin(coreAndroidPluginName);
+ //get live CallbackContext
+ CallbackContext liveCallbackContext = (CallbackContext) messageChannelField.get(liveAppPlugin);
+
+ //get callback id from live callbackcontext
+ String liveCallbackId = (liveCallbackContext != null) ? liveCallbackContext.getCallbackId() : null;
+ //get callback id from cached callbackcontext
+ String cachedCallbackId = (cachedCallbackContext != null) ? cachedCallbackContext.getCallbackId() : null;
+
+ //verify that the live message channel has been initialized
+ assertNotNull(liveCallbackId);
+ //verify that the cached message channel and the live message channel have the same id
+ assertEquals(liveCallbackId, cachedCallbackId);
+ }
+
+ private void sleep(int timeout) {
+ try {
+ Thread.sleep(timeout);
+ } catch (InterruptedException e) {
+ fail("Unexpected Timeout");
+ }
+ }
+}
diff --git a/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/StandardActivityTest.java b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/StandardActivityTest.java
new file mode 100644
index 00000000..e0b4de05
--- /dev/null
+++ b/test/androidx/app/src/androidTest/java/org/apache/cordova/unittests/StandardActivityTest.java
@@ -0,0 +1,100 @@
+/*
+ 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.unittests;
+
+import android.content.Intent;
+import android.graphics.Color;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import org.apache.cordova.CordovaPreferences;
+import org.apache.cordova.CordovaWebView;
+import org.apache.cordova.engine.SystemWebView;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNotSame;
+import static junit.framework.Assert.assertTrue;
+
+/**
+ * The purpose of this test is to test the default application that is generated by Cordova itself
+ *
+ */
+@RunWith(AndroidJUnit4.class)
+public class StandardActivityTest {
+
+ private static final String FALSE_URI = "http://www.google.com";
+
+ // Don't launch the activity, we're going to send it intents
+ @Rule
+ public ActivityTestRule mActivityRule = new ActivityTestRule<>(
+ StandardActivity.class, true, false);
+
+ @Before
+ public void launchApplicationWithIntent() {
+ Intent intent = new Intent();
+ intent.putExtra("startUrl", FALSE_URI);
+ intent.putExtra("backgroundcolor", "#0000ff");
+ mActivityRule.launchActivity(intent);
+ }
+
+
+ @Test
+ public void webViewCheck() {
+ StandardActivity activity = (StandardActivity) mActivityRule.getActivity();
+ //Fish the webview out of the mostly locked down Activity using the Android SDK
+ View view = activity.getWindow().getCurrentFocus();
+ assertEquals(SystemWebView.class, view.getClass());
+ }
+
+ @Test
+ public void startUriIntentCheck() {
+ StandardActivity activity = (StandardActivity) mActivityRule.getActivity();
+ final SystemWebView webView = (SystemWebView) activity.getWindow().getCurrentFocus();
+ try {
+ mActivityRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ String uri = webView.getUrl();
+ assertFalse(uri.equals(FALSE_URI));
+ }
+ });
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+ }
+
+ @Test
+ public void checkBackgroundIntentCheck() {
+ StandardActivity activity = (StandardActivity) mActivityRule.getActivity();
+ final SystemWebView webView = (SystemWebView) activity.getWindow().getCurrentFocus();
+ CordovaWebView webInterface = webView.getCordovaWebView();
+ CordovaPreferences prefs = webInterface.getPreferences();
+ assertFalse(prefs.getInteger("backgroundcolor", Color.BLACK) == Color.GREEN);
+ }
+
+}
diff --git a/test/androidx/app/src/main/AndroidManifest.xml b/test/androidx/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..9d404fe6
--- /dev/null
+++ b/test/androidx/app/src/main/AndroidManifest.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/androidx/app/src/main/assets/www/backbuttonmultipage/index.html b/test/androidx/app/src/main/assets/www/backbuttonmultipage/index.html
new file mode 100755
index 00000000..8739aa0e
--- /dev/null
+++ b/test/androidx/app/src/main/assets/www/backbuttonmultipage/index.html
@@ -0,0 +1,40 @@
+
+
+
+
+Backbutton
+
+
+
+
+
+
Cordova Android Tests
+
+
Cordova:
+
Deviceready:
+
+
+
Page 1
+ Go to next page.
+ If returning from previous page, press "backbutton". You should exit this app.
+
+ Press the 3 buttons below. You should stay on same page.
+ Press "backbutton" 4 times. This will go back to #test3, #test2, #test1, then return to previous Page 2.
+
+ This is an expected error page because the initial href doesn't exist.
+
+
diff --git a/test/androidx/app/src/main/assets/www/iframe/index.html b/test/androidx/app/src/main/assets/www/iframe/index.html
new file mode 100755
index 00000000..c286e245
--- /dev/null
+++ b/test/androidx/app/src/main/assets/www/iframe/index.html
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+ Cordova Tests
+
+
+
+
+
+
+
IFrame
+
+
Cordova:
+
Deviceready:
+
+
+ Press the two buttons:
+ 1. Google Maps should display in iframe.
+ 2. Page 2 replaces current page.
+ (NOTE: THIS BEHAVIOR IS WRONG - AND NEEDS TO BE FIXED IN FUTURE RELEASE.)
+
+ This should display a Cordova page inside an iframe. The info above should be filled out.
+ (NOTE: THIS DOES NOT WORK AND NEEDS TO BE FIXED IN FUTURE RELEASE.)
+
+ Press "Home" button, then return to this app to see pause/resume.
+ There should be "Running" entries between pause and resume since app continues to run in the background.
+
Test 2
+ Press "Load new page" button to load a new Cordova page.
+ When returning, you should see
+
+
+ Load new page
+ Clear status
+
+
+
+
+
+
diff --git a/test/androidx/app/src/main/assets/www/main.js b/test/androidx/app/src/main/assets/www/main.js
new file mode 100755
index 00000000..a2421391
--- /dev/null
+++ b/test/androidx/app/src/main/assets/www/main.js
@@ -0,0 +1,27 @@
+/*
+ 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 deviceInfo = function() {
+ document.getElementById("deviceready").innerHTML = "fired";
+ document.getElementById("cordova").innerHTML = cordova.version;
+};
+
+function init() {
+ document.addEventListener("deviceready", deviceInfo, true);
+}
diff --git a/test/androidx/app/src/main/assets/www/master.css b/test/androidx/app/src/main/assets/www/master.css
new file mode 100755
index 00000000..c3e3c452
--- /dev/null
+++ b/test/androidx/app/src/main/assets/www/master.css
@@ -0,0 +1,136 @@
+/*
+ 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.
+*/
+
+ body {
+ background:#222 none repeat scroll 0 0;
+ color:#666;
+ font-family:Helvetica;
+ font-size:72%;
+ line-height:1.5em;
+ margin:0;
+ border-top:1px solid #393939;
+ }
+
+ #info{
+ background:#ffa;
+ border: 1px solid #ffd324;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ clear:both;
+ margin:15px 6px 0;
+ width:295px;
+ padding:4px 0px 2px 10px;
+ }
+
+ #info > h4{
+ font-size:.95em;
+ margin:5px 0;
+ }
+
+ #stage.theme{
+ padding-top:3px;
+ }
+
+ /* Definition List */
+ #stage.theme > dl{
+ padding-top:10px;
+ clear:both;
+ margin:0;
+ list-style-type:none;
+ padding-left:10px;
+ overflow:auto;
+ }
+
+ #stage.theme > dl > dt{
+ font-weight:bold;
+ float:left;
+ margin-left:5px;
+ }
+
+ #stage.theme > dl > dd{
+ width:45px;
+ float:left;
+ color:#a87;
+ font-weight:bold;
+ }
+
+ /* Content Styling */
+ #stage.theme > h1, #stage.theme > h2, #stage.theme > p{
+ margin:1em 0 .5em 13px;
+ }
+
+ #stage.theme > h1{
+ color:#eee;
+ font-size:1.6em;
+ text-align:center;
+ margin:0;
+ margin-top:15px;
+ padding:0;
+ }
+
+ #stage.theme > h2{
+ clear:both;
+ margin:0;
+ padding:3px;
+ font-size:1em;
+ text-align:center;
+ }
+
+ /* Stage Buttons */
+ #stage.theme a.btn{
+ border: 1px solid #555;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ text-align:center;
+ display:block;
+ float:left;
+ background:#444;
+ width:150px;
+ color:#9ab;
+ font-size:1.1em;
+ text-decoration:none;
+ padding:1.2em 0;
+ margin:3px 0px 3px 5px;
+ }
+ #stage.theme a.btn.large{
+ width:308px;
+ padding:1.2em 0;
+ }
+
+ /* Stage Buttons */
+ #stage.theme button.btn{
+ border: 1px solid #555;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ text-align:center;
+ display:block;
+ float:left;
+ background:#444;
+ width:150px;
+ color:#9ab;
+ font-size:1.1em;
+ text-decoration:none;
+ padding:1.2em 0;
+ margin:3px 0px 3px 5px;
+ }
+#stage.theme button.btn.large{
+ width:308px;
+ padding:1.2em 0;
+ }
+
diff --git a/test/androidx/app/src/main/assets/www/whitelist/index.html b/test/androidx/app/src/main/assets/www/whitelist/index.html
new file mode 100755
index 00000000..b9596c93
--- /dev/null
+++ b/test/androidx/app/src/main/assets/www/whitelist/index.html
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+ Cordova Tests
+
+
+
+
+
+
Whitelist Page 1
+
+
Cordova:
+
Deviceready:
+
+
+ Loading Page 2 should be successful.
+ Loading Page 3 should be in web browser.
+ Loading Page 2 with target=_blank should be in web browser?
+ (THIS DOESN'T HAPPEN.) https://issues.apache.org/jira/browse/CB-362
+
+
+
diff --git a/test/androidx/app/src/main/java/org/apache/cordova/unittests/EmbeddedWebViewActivity.java b/test/androidx/app/src/main/java/org/apache/cordova/unittests/EmbeddedWebViewActivity.java
new file mode 100644
index 00000000..8de739e0
--- /dev/null
+++ b/test/androidx/app/src/main/java/org/apache/cordova/unittests/EmbeddedWebViewActivity.java
@@ -0,0 +1,110 @@
+/*
+ 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.unittests;
+
+import android.content.Intent;
+import androidx.appcompat.app.AppCompatActivity;
+import android.os.Bundle;
+import android.util.Log;
+
+import org.apache.cordova.ConfigXmlParser;
+import org.apache.cordova.CordovaInterfaceImpl;
+import org.apache.cordova.CordovaWebView;
+import org.apache.cordova.CordovaWebViewImpl;
+import org.apache.cordova.PluginManager;
+import org.apache.cordova.engine.SystemWebView;
+import org.apache.cordova.engine.SystemWebViewEngine;
+import org.json.JSONException;
+
+public class EmbeddedWebViewActivity extends AppCompatActivity {
+
+ public CordovaWebView webInterface;
+ private CordovaInterfaceImpl cordovaInterface = new CordovaInterfaceImpl(this);
+ private String TAG = "CordovaTestActivity";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ //Set up the webview
+ ConfigXmlParser parser = new ConfigXmlParser();
+ parser.parse(this);
+
+ SystemWebView webView = (SystemWebView) findViewById(R.id.cordovaWebView);
+ webInterface = new CordovaWebViewImpl(new SystemWebViewEngine(webView));
+ webInterface.init(cordovaInterface, parser.getPluginEntries(), parser.getPreferences());
+
+ webView.loadUrl(parser.getLaunchUrl());
+ }
+
+ // This is still required by Cordova
+ @Override
+ public void onDestroy()
+ {
+ super.onDestroy();
+ PluginManager pluginManager = webInterface.getPluginManager();
+ if(pluginManager != null)
+ {
+ pluginManager.onDestroy();
+ }
+
+ }
+
+ /**
+ * Called when an activity you launched exits, giving you the requestCode you started it with,
+ * the resultCode it returned, and any additional data from it.
+ *
+ * @param requestCode The request code originally supplied to startActivityForResult(),
+ * allowing you to identify who this result came from.
+ * @param resultCode The integer result code returned by the child activity through its setResult().
+ * @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
+ */
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ super.onActivityResult(requestCode, resultCode, intent);
+ cordovaInterface.onActivityResult(requestCode, resultCode, intent);
+ }
+
+ /**
+ * Called by the system when the user grants permissions!
+ *
+ * Note: The fragment gets priority over the activity, since the activity doesn't call
+ * into the parent onRequestPermissionResult, which is why there's no override.
+ *
+ * @param requestCode
+ * @param permissions
+ * @param grantResults
+ */
+ public void onRequestPermissionsResult(int requestCode, String permissions[],
+ int[] grantResults) {
+ try
+ {
+ cordovaInterface.onRequestPermissionResult(requestCode, permissions, grantResults);
+ }
+ catch (JSONException e)
+ {
+ Log.d(TAG, "JSONException: Parameters fed into the method are not valid");
+ e.printStackTrace();
+ }
+
+ }
+
+}
diff --git a/test/androidx/app/src/main/java/org/apache/cordova/unittests/LifeCyclePlugin.java b/test/androidx/app/src/main/java/org/apache/cordova/unittests/LifeCyclePlugin.java
new file mode 100644
index 00000000..deaf74d2
--- /dev/null
+++ b/test/androidx/app/src/main/java/org/apache/cordova/unittests/LifeCyclePlugin.java
@@ -0,0 +1,49 @@
+/*
+ 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.unittests;
+
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.LOG;
+
+public class LifeCyclePlugin extends CordovaPlugin {
+
+ static String TAG = "LifeCyclePlugin";
+ String calls = "";
+
+ @Override
+ public void onStart() {
+ calls += "start,";
+ LOG.d(TAG, "onStart");
+ }
+ @Override
+ public void onPause(boolean multitasking) {
+ calls += "pause,";
+ LOG.d(TAG, "onPause");
+ }
+ @Override
+ public void onResume(boolean multitasking) {
+ calls += "resume,";
+ LOG.d(TAG, "onResume");
+ }
+ @Override
+ public void onStop() {
+ calls += "stop,";
+ LOG.d(TAG, "onStop");
+ }
+}
diff --git a/test/androidx/app/src/main/java/org/apache/cordova/unittests/StandardActivity.java b/test/androidx/app/src/main/java/org/apache/cordova/unittests/StandardActivity.java
new file mode 100644
index 00000000..9e7cce94
--- /dev/null
+++ b/test/androidx/app/src/main/java/org/apache/cordova/unittests/StandardActivity.java
@@ -0,0 +1,42 @@
+/*
+ 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.unittests;
+
+import android.os.Bundle;
+
+import org.apache.cordova.CordovaActivity;
+
+public class StandardActivity extends CordovaActivity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ // enable Cordova apps to be started in the background
+ Bundle extras = getIntent().getExtras();
+ if (extras != null && extras.getBoolean("cdvStartInBackground", false)) {
+ moveTaskToBack(true);
+ }
+
+ // Set by in config.xml
+ loadUrl(launchUrl);
+ }
+}
diff --git a/test/androidx/app/src/main/java/org/apache/cordova/unittests/TestActivity.java b/test/androidx/app/src/main/java/org/apache/cordova/unittests/TestActivity.java
new file mode 100644
index 00000000..024a131f
--- /dev/null
+++ b/test/androidx/app/src/main/java/org/apache/cordova/unittests/TestActivity.java
@@ -0,0 +1,67 @@
+/*
+ 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.unittests;
+
+import android.os.Bundle;
+
+import org.apache.cordova.CordovaActivity;
+import org.apache.cordova.CordovaWebView;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.RunnableFuture;
+
+/**
+ * The purpose of this activity is to allow the test framework to manipulate the start url, which
+ * is normally locked down by CordovaActivity to standard users who aren't editing their Java code.
+ */
+
+public class TestActivity extends CordovaActivity {
+
+ public final ArrayBlockingQueue onPageFinishedUrl = new ArrayBlockingQueue(500);
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // enable Cordova apps to be started in the background
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ if (extras.getBoolean("cdvStartInBackground", false)) {
+ moveTaskToBack(true);
+ }
+ launchUrl = extras.getString("startUrl", "index.html");
+ }
+
+ // Set by in config.xml
+ loadUrl(launchUrl);
+ }
+
+ @Override
+ public Object onMessage(String id, Object data) {
+ if ("onPageFinished".equals(id)) {
+ onPageFinishedUrl.add((String) data);
+ }
+ return super.onMessage(id, data);
+ }
+
+ public CordovaWebView getWebInterface() { return this.appView; }
+
+}
diff --git a/test/androidx/app/src/main/res/layout/activity_main.xml b/test/androidx/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..7cbd51d6
--- /dev/null
+++ b/test/androidx/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
diff --git a/test/androidx/app/src/main/res/mipmap-hdpi/ic_launcher.png b/test/androidx/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..cde69bcc
Binary files /dev/null and b/test/androidx/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/test/androidx/app/src/main/res/mipmap-mdpi/ic_launcher.png b/test/androidx/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..c133a0cb
Binary files /dev/null and b/test/androidx/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/test/androidx/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/test/androidx/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..bfa42f0e
Binary files /dev/null and b/test/androidx/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/test/androidx/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/test/androidx/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..324e72cd
Binary files /dev/null and b/test/androidx/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/test/androidx/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/test/androidx/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..aee44e13
Binary files /dev/null and b/test/androidx/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/test/androidx/app/src/main/res/values-w820dp/dimens.xml b/test/androidx/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 00000000..a1a90b9f
--- /dev/null
+++ b/test/androidx/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,24 @@
+
+
+
+ 64dp
+
diff --git a/test/androidx/app/src/main/res/values/colors.xml b/test/androidx/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..b184be6b
--- /dev/null
+++ b/test/androidx/app/src/main/res/values/colors.xml
@@ -0,0 +1,24 @@
+
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/test/androidx/app/src/main/res/values/dimens.xml b/test/androidx/app/src/main/res/values/dimens.xml
new file mode 100644
index 00000000..8f9a14a9
--- /dev/null
+++ b/test/androidx/app/src/main/res/values/dimens.xml
@@ -0,0 +1,23 @@
+
+
+
+ 16dp
+ 16dp
+
diff --git a/test/androidx/app/src/main/res/values/strings.xml b/test/androidx/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..353e3abc
--- /dev/null
+++ b/test/androidx/app/src/main/res/values/strings.xml
@@ -0,0 +1,22 @@
+
+
+
+ UnitTests
+
diff --git a/test/androidx/app/src/main/res/values/styles.xml b/test/androidx/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000..ae304dac
--- /dev/null
+++ b/test/androidx/app/src/main/res/values/styles.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
diff --git a/test/androidx/app/src/main/res/xml/config.xml b/test/androidx/app/src/main/res/xml/config.xml
new file mode 100644
index 00000000..a824011e
--- /dev/null
+++ b/test/androidx/app/src/main/res/xml/config.xml
@@ -0,0 +1,44 @@
+
+
+
+ Hello Cordova
+
+ A sample Apache Cordova application that responds to the deviceready event.
+
+
+ Apache Cordova Team
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/androidx/app/src/test/java/org/apache/cordova/unittests/NativeToJsMessageQueueTest.java b/test/androidx/app/src/test/java/org/apache/cordova/unittests/NativeToJsMessageQueueTest.java
new file mode 100644
index 00000000..dc7bb672
--- /dev/null
+++ b/test/androidx/app/src/test/java/org/apache/cordova/unittests/NativeToJsMessageQueueTest.java
@@ -0,0 +1,177 @@
+/*
+ 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.unittests;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.view.View;
+import android.webkit.ValueCallback;
+
+import org.apache.cordova.CordovaInterface;
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.CordovaResourceApi;
+import org.apache.cordova.CordovaWebView;
+import org.apache.cordova.CordovaWebViewEngine;
+import org.apache.cordova.ICordovaCookieManager;
+import org.apache.cordova.NativeToJsMessageQueue;
+import org.apache.cordova.PluginManager;
+import org.apache.cordova.PluginResult;
+import org.apache.cordova.engine.SystemWebViewEngine;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.concurrent.ExecutorService;
+
+import static org.junit.Assert.*;
+
+public class NativeToJsMessageQueueTest {
+
+ NativeToJsMessageQueue queue = new NativeToJsMessageQueue();
+ private String TEST_CALLBACK_ID = "MessageQueueTest";
+
+ //A queue with no bridges should not work
+ @Test
+ public void testEmptyBridge()
+ {
+ assertFalse(queue.isBridgeEnabled());
+ }
+
+
+ //A queue with at least one bridge should work, using Eval Bridge
+ @Test
+ public void testEnabledBridge()
+ {
+ NativeToJsMessageQueue.BridgeMode bridge;
+ bridge = new NativeToJsMessageQueue.NoOpBridgeMode();
+ queue.addBridgeMode(bridge);
+ queue.setBridgeMode(0);
+ assertTrue(queue.isBridgeEnabled());
+ }
+
+ //This test is for the undocumented encoding system setup for the bridge
+ //TODO: Document how the non-Javascript bridges are supposed to work
+ @Test
+ public void testPopAndEncode()
+ {
+ NativeToJsMessageQueue.BridgeMode bridge;
+ bridge = new NativeToJsMessageQueue.NoOpBridgeMode();
+ queue.addBridgeMode(bridge);
+ queue.setBridgeMode(0);
+
+ PluginResult result = new PluginResult(PluginResult.Status.OK);
+ queue.addPluginResult(result, TEST_CALLBACK_ID);
+ assertFalse(queue.isEmpty());
+ String resultString = queue.popAndEncode(false);
+ String [] results = resultString.split(" ");
+ assertEquals(TEST_CALLBACK_ID, results[2]);
+ }
+
+ //This test is for the evalBridge, which directly calls cordova.callbackFromNative, skipping
+ //platform specific NativeToJs code
+ @Test
+ public void testBasicPopAndEncodeAsJs()
+ {
+ NativeToJsMessageQueue.BridgeMode bridge;
+ bridge = new NativeToJsMessageQueue.NoOpBridgeMode();
+ queue.addBridgeMode(bridge);
+ queue.setBridgeMode(0);
+
+ PluginResult result = new PluginResult(PluginResult.Status.OK);
+ queue.addPluginResult(result, TEST_CALLBACK_ID);
+ assertFalse(queue.isEmpty());
+ String resultString = queue.popAndEncodeAsJs();
+ assertTrue(resultString.startsWith("cordova.callbackFromNative"));
+ }
+
+ //This test is for the evalBridge, which directly calls cordova.callbackFromNative, skipping
+ //platform specific NativeToJs code
+ @Test
+ public void testStringPopAndEncodeAsJs()
+ {
+ NativeToJsMessageQueue.BridgeMode bridge;
+ bridge = new NativeToJsMessageQueue.NoOpBridgeMode();
+ queue.addBridgeMode(bridge);
+ queue.setBridgeMode(0);
+
+ PluginResult result = new PluginResult(PluginResult.Status.OK, "String Plugin Result");
+ queue.addPluginResult(result, TEST_CALLBACK_ID);
+ assertFalse(queue.isEmpty());
+ String resultString = queue.popAndEncodeAsJs();
+ assertTrue(resultString.startsWith("cordova.callbackFromNative"));
+ }
+
+ //This test is for the evalBridge, which directly calls cordova.callbackFromNative, skipping
+ //platform specific NativeToJs code
+ @Test
+ public void testJsonPopAndEncodeAsJs()
+ {
+ NativeToJsMessageQueue.BridgeMode bridge;
+ bridge = new NativeToJsMessageQueue.NoOpBridgeMode();
+ queue.addBridgeMode(bridge);
+ queue.setBridgeMode(0);
+
+ JSONObject object = new JSONObject();
+ try {
+ object.put("test", "value");
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ PluginResult result = new PluginResult(PluginResult.Status.OK, object);
+ queue.addPluginResult(result, TEST_CALLBACK_ID);
+ assertFalse(queue.isEmpty());
+ String resultString = queue.popAndEncodeAsJs();
+ assertTrue(resultString.startsWith("cordova.callbackFromNative"));
+ }
+
+ //This test is for the evalBridge, which directly calls cordova.callbackFromNative, skipping
+ //platform specific NativeToJs code
+ @Test
+ public void testMultipartPopAndEncodeAsJs()
+ {
+ ArrayList multiparts = new ArrayList();
+ for (int i=0; i<5; i++) {
+ multiparts.add(new PluginResult(PluginResult.Status.OK, i));
+ }
+ PluginResult multipartresult = new PluginResult(PluginResult.Status.OK, multiparts);
+ NativeToJsMessageQueue queue = new NativeToJsMessageQueue();
+ queue.addBridgeMode(new NativeToJsMessageQueue.NoOpBridgeMode());
+ queue.setBridgeMode(0);
+ queue.addPluginResult(multipartresult, "37");
+ String result = queue.popAndEncodeAsJs();
+ assertEquals(result, "cordova.callbackFromNative('37',true,1,[0,1,2,3,4],false);");
+ }
+
+ @Test
+ public void testNullPopAndEncodeAsJs()
+ {
+ NativeToJsMessageQueue queue = new NativeToJsMessageQueue();
+ queue.addBridgeMode(new NativeToJsMessageQueue.NoOpBridgeMode());
+ queue.setBridgeMode(0);
+
+ PluginResult result = new PluginResult(PluginResult.Status.OK, (String)null);
+ queue.addPluginResult(result, TEST_CALLBACK_ID);
+ assertFalse(queue.isEmpty());
+ String resultString = queue.popAndEncodeAsJs();
+ assertEquals(resultString, "cordova.callbackFromNative('" + TEST_CALLBACK_ID + "',true,1,[null],false);");
+ }
+}
diff --git a/test/androidx/build.gradle b/test/androidx/build.gradle
new file mode 100644
index 00000000..c9bf1e7d
--- /dev/null
+++ b/test/androidx/build.gradle
@@ -0,0 +1,44 @@
+/* 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.
+*/
+
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+
+ classpath 'com.android.tools.build:gradle:3.3.0'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/test/androidx/gradle.properties b/test/androidx/gradle.properties
new file mode 100644
index 00000000..30d2ba91
--- /dev/null
+++ b/test/androidx/gradle.properties
@@ -0,0 +1,19 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+android.useAndroidX=true
+android.enableJetifier=true
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/test/androidx/settings.gradle b/test/androidx/settings.gradle
new file mode 100644
index 00000000..ac3d9ebd
--- /dev/null
+++ b/test/androidx/settings.gradle
@@ -0,0 +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.
+*/
+
+include ':app'
+include ":CordovaLib"
+project(':CordovaLib').projectDir = new File('../../framework')
diff --git a/test/androidx/wrapper.gradle b/test/androidx/wrapper.gradle
new file mode 100644
index 00000000..5dd3dbca
--- /dev/null
+++ b/test/androidx/wrapper.gradle
@@ -0,0 +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.
+*/
+
+wrapper {
+ gradleVersion = '4.10.3'
+}
diff --git a/test/run_java_unit_tests.js b/test/run_java_unit_tests.js
index 7e8b9b14..40354b58 100644
--- a/test/run_java_unit_tests.js
+++ b/test/run_java_unit_tests.js
@@ -19,18 +19,55 @@
under the License.
*/
-var path = require('path');
-var execa = require('execa');
-var ProjectBuilder = require('../bin/templates/cordova/lib/builders/ProjectBuilder');
+const path = require('path');
+const execa = require('execa');
+const ProjectBuilder = require('../bin/templates/cordova/lib/builders/ProjectBuilder');
+
+class AndroidTestRunner {
+ constructor (testTitle, projectDir) {
+ this.testTitle = testTitle;
+ this.projectDir = projectDir;
+ this.gradleWrapper = path.join(this.projectDir, 'gradlew');
+ }
+
+ _gradlew (...args) {
+ return execa(
+ this.gradleWrapper,
+ args,
+ {
+ stdio: 'inherit',
+ cwd: this.projectDir
+ }
+ );
+ }
+
+ _createProjectBuilder () {
+ return new ProjectBuilder(this.projectDir).runGradleWrapper('gradle');
+ }
+
+ run () {
+ return Promise.resolve()
+ .then(_ => console.log(`[${this.testTitle}] Preparing Gradle wrapper for Java unit tests.`))
+ .then(_ => this._createProjectBuilder())
+ .then(_ => this._gradlew('--version'))
+ .then(_ => console.log(`[${this.testTitle}] Gradle wrapper is ready. Running tests now.`))
+ .then(_ => this._gradlew('test'))
+ .then(_ => console.log(`[${this.testTitle}] Java unit tests completed successfully`));
+ }
+}
Promise.resolve()
- .then(_ => console.log('Preparing Gradle wrapper for Java unit tests.'))
- .then(_ => new ProjectBuilder(__dirname).runGradleWrapper('gradle'))
- .then(_ => gradlew('--version'))
+ .then(_ => console.log('Starting to run all android platform tests'))
- .then(_ => console.log('Gradle wrapper is ready. Running tests now.'))
- .then(_ => gradlew('test'))
- .then(_ => console.log('Java unit tests completed successfully.'));
+ // Android Test
+ .then(_ => new AndroidTestRunner('Android Project', path.resolve(__dirname, 'android')))
+ .then(test => test.run())
+
+ // AndroidX Test
+ .then(_ => new AndroidTestRunner('AndroidX Project', path.resolve(__dirname, 'androidx')))
+ .then(test => test.run())
+
+ .then(_ => console.log('Finished running all android platform tests'));
process.on('unhandledRejection', err => {
// If err has a stderr property, we have seen the message already
@@ -38,11 +75,3 @@ process.on('unhandledRejection', err => {
console.error('JAVA UNIT TESTS FAILED!');
process.exitCode = err.code || 1;
});
-
-function gradlew () {
- const wrapperPath = path.join(__dirname, 'gradlew');
- return execa(wrapperPath, Array.from(arguments), {
- stdio: 'inherit',
- cwd: __dirname
- });
-}