Jelajahi Sumber

Merge pull request #110829 from m4gr3d/javaclasswrapper_regression_tests

Add Android instrumented tests to the `app` module
Thaddeus Crews 2 hari lalu
induk
melakukan
cc008b288a
100 mengubah file dengan 997 tambahan dan 41 penghapusan
  1. 4 4
      .pre-commit-config.yaml
  2. 6 6
      COPYRIGHT.txt
  3. 1 1
      core/error/error_list.h
  4. 2 2
      platform/android/export/export_plugin.cpp
  5. 1 1
      platform/android/export/gradle_export_util.h
  6. 20 10
      platform/android/java/app/build.gradle
  7. 76 0
      platform/android/java/app/src/androidTestInstrumented/java/com/godot/game/GodotAppTest.kt
  8. 9 0
      platform/android/java/app/src/instrumented/AndroidManifest.xml
  9. 2 0
      platform/android/java/app/src/instrumented/assets/.gitattributes
  10. 3 0
      platform/android/java/app/src/instrumented/assets/.gitignore
  11. 1 0
      platform/android/java/app/src/instrumented/assets/.godot/.gdignore
  12. 17 0
      platform/android/java/app/src/instrumented/assets/.godot/global_script_class_cache.cfg
  13. TEMPAT SAMPAH
      platform/android/java/app/src/instrumented/assets/.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex
  14. 2 0
      platform/android/java/app/src/instrumented/assets/.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.md5
  15. 0 0
      platform/android/java/app/src/instrumented/assets/.godot/scene_groups_cache.cfg
  16. TEMPAT SAMPAH
      platform/android/java/app/src/instrumented/assets/.godot/uid_cache.bin
  17. 1 0
      platform/android/java/app/src/instrumented/assets/icon.svg
  18. 43 0
      platform/android/java/app/src/instrumented/assets/icon.svg.import
  19. 60 0
      platform/android/java/app/src/instrumented/assets/main.gd
  20. 1 0
      platform/android/java/app/src/instrumented/assets/main.gd.uid
  21. 34 0
      platform/android/java/app/src/instrumented/assets/main.tscn
  22. 26 0
      platform/android/java/app/src/instrumented/assets/project.godot
  23. 44 0
      platform/android/java/app/src/instrumented/assets/test/base_test.gd
  24. 1 0
      platform/android/java/app/src/instrumented/assets/test/base_test.gd.uid
  25. 136 0
      platform/android/java/app/src/instrumented/assets/test/javaclasswrapper/java_class_wrapper_tests.gd
  26. 1 0
      platform/android/java/app/src/instrumented/assets/test/javaclasswrapper/java_class_wrapper_tests.gd.uid
  27. 144 0
      platform/android/java/app/src/instrumented/java/com/godot/game/test/GodotAppInstrumentedTestPlugin.kt
  28. 261 0
      platform/android/java/app/src/instrumented/java/com/godot/game/test/javaclasswrapper/TestClass.kt
  29. 40 0
      platform/android/java/app/src/instrumented/java/com/godot/game/test/javaclasswrapper/TestClass2.kt
  30. 40 0
      platform/android/java/app/src/instrumented/java/com/godot/game/test/javaclasswrapper/TestClass3.kt
  31. 4 0
      platform/android/java/app/src/instrumented/res/values/strings.xml
  32. 0 0
      platform/android/java/app/src/main/AndroidManifest.xml
  33. 0 0
      platform/android/java/app/src/main/assets/.gitignore
  34. 9 0
      platform/android/java/app/src/main/java/com/godot/game/GodotApp.java
  35. 6 15
      platform/android/java/lib/build.gradle
  36. 0 0
      platform/android/java/lib/src/main/AndroidManifest.xml
  37. 0 0
      platform/android/java/lib/src/main/aidl/com/android/vending/licensing/ILicenseResultListener.aidl
  38. 0 0
      platform/android/java/lib/src/main/aidl/com/android/vending/licensing/ILicensingService.aidl
  39. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/Constants.java
  40. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/DownloadProgressInfo.java
  41. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/DownloaderClientMarshaller.java
  42. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/DownloaderServiceMarshaller.java
  43. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/Helpers.java
  44. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/IDownloaderClient.java
  45. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/IDownloaderService.java
  46. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/IStub.java
  47. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/SystemFacade.java
  48. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/impl/CustomIntentService.java
  49. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/impl/DownloadInfo.java
  50. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java
  51. 1 1
      platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/impl/DownloadThread.java
  52. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/impl/DownloaderService.java
  53. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/impl/DownloadsDB.java
  54. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/impl/HttpDateTime.java
  55. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/licensing/AESObfuscator.java
  56. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/licensing/APKExpansionPolicy.java
  57. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/licensing/DeviceLimiter.java
  58. 1 1
      platform/android/java/lib/src/main/java/com/google/android/vending/licensing/LicenseChecker.java
  59. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/licensing/LicenseCheckerCallback.java
  60. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/licensing/LicenseValidator.java
  61. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/licensing/NullDeviceLimiter.java
  62. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/licensing/Obfuscator.java
  63. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/licensing/Policy.java
  64. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/licensing/PreferenceObfuscator.java
  65. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/licensing/ResponseData.java
  66. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/licensing/ServerManagedPolicy.java
  67. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/licensing/StrictPolicy.java
  68. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/licensing/ValidationException.java
  69. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/licensing/util/Base64.java
  70. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/licensing/util/Base64DecoderException.java
  71. 0 0
      platform/android/java/lib/src/main/java/com/google/android/vending/licensing/util/URIQueryDecoder.java
  72. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/Dictionary.java
  73. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/FullScreenGodotApp.java
  74. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/Godot.kt
  75. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/GodotActivity.kt
  76. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/GodotDownloaderAlarmReceiver.java
  77. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/GodotDownloaderService.java
  78. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/GodotFragment.java
  79. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/GodotGLRenderView.java
  80. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/GodotHost.java
  81. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/GodotIO.java
  82. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/GodotLib.java
  83. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/GodotRenderView.java
  84. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/GodotVulkanRenderView.java
  85. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/editor/utils/EditorUtils.kt
  86. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/editor/utils/GameMenuUtils.kt
  87. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/error/Error.kt
  88. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/gl/EGLLogWrapper.java
  89. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/gl/GLSurfaceView.java
  90. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/gl/GodotRenderer.java
  91. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/input/GodotEditText.java
  92. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/input/GodotGestureHandler.kt
  93. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/input/GodotInputHandler.java
  94. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/input/GodotTextInputWrapper.java
  95. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/input/InputEventRunnable.java
  96. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/input/Joystick.java
  97. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/io/FilePicker.kt
  98. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/io/StorageScope.kt
  99. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/io/directory/AssetsDirectoryAccess.kt
  100. 0 0
      platform/android/java/lib/src/main/java/org/godotengine/godot/io/directory/DirectoryAccessHandler.kt

+ 4 - 4
.pre-commit-config.yaml

@@ -6,7 +6,7 @@ exclude: |
     .*thirdparty/.*|
     .*thirdparty/.*|
     .*-(dll|dylib|so)_wrap\.[ch]|
     .*-(dll|dylib|so)_wrap\.[ch]|
     platform/android/java/editor/src/main/java/com/android/.*|
     platform/android/java/editor/src/main/java/com/android/.*|
-    platform/android/java/lib/src/com/google/.*
+    platform/android/java/lib/src/main/java/com/google/.*
   )$
   )$
 
 
 repos:
 repos:
@@ -148,9 +148,9 @@ repos:
           (?x)^(
           (?x)^(
             core/math/bvh_.*\.inc|
             core/math/bvh_.*\.inc|
             platform/(?!android|ios|linuxbsd|macos|web|windows)\w+/.*|
             platform/(?!android|ios|linuxbsd|macos|web|windows)\w+/.*|
-            platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView\.java|
-            platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper\.java|
-            platform/android/java/lib/src/org/godotengine/godot/utils/ProcessPhoenix\.java
+            platform/android/java/lib/src/main/java/org/godotengine/godot/gl/GLSurfaceView\.java|
+            platform/android/java/lib/src/main/java/org/godotengine/godot/gl/EGLLogWrapper\.java|
+            platform/android/java/lib/src/main/java/org/godotengine/godot/utils/ProcessPhoenix\.java
           )$
           )$
 
 
       - id: header-guards
       - id: header-guards

+ 6 - 6
COPYRIGHT.txt

@@ -123,17 +123,17 @@ Copyright: 2020, Manuel Prandini
 License: Expat
 License: Expat
 
 
 Files: platform/android/java/editor/src/main/java/com/android/*
 Files: platform/android/java/editor/src/main/java/com/android/*
- platform/android/java/lib/aidl/com/android/*
- platform/android/java/lib/res/layout/status_bar_ongoing_event_progress_bar.xml
- platform/android/java/lib/src/com/google/android/*
- platform/android/java/lib/src/org/godotengine/godot/input/InputManagerCompat.java
- platform/android/java/lib/src/org/godotengine/godot/input/InputManagerV16.java
+ platform/android/java/lib/src/main/aidl/com/android/*
+ platform/android/java/lib/src/main/res/layout/status_bar_ongoing_event_progress_bar.xml
+ platform/android/java/lib/src/main/java/com/google/android/*
+ platform/android/java/lib/src/main/java/org/godotengine/godot/input/InputManagerCompat.java
+ platform/android/java/lib/src/main/java/org/godotengine/godot/input/InputManagerV16.java
 Comment: The Android Open Source Project
 Comment: The Android Open Source Project
 Copyright: 2008-2016, The Android Open Source Project
 Copyright: 2008-2016, The Android Open Source Project
  2002, Google, Inc.
  2002, Google, Inc.
 License: Apache-2.0
 License: Apache-2.0
 
 
-Files: platform/android/java/lib/src/org/godotengine/godot/utils/ProcessPhoenix.java
+Files: platform/android/java/lib/src/main/java/org/godotengine/godot/utils/ProcessPhoenix.java
 Comment: ProcessPhoenix
 Comment: ProcessPhoenix
 Copyright: 2015, Jake Wharton
 Copyright: 2015, Jake Wharton
 License: Apache-2.0
 License: Apache-2.0

+ 1 - 1
core/error/error_list.h

@@ -40,7 +40,7 @@
  * - Are added to the Error enum in core/error/error_list.h
  * - Are added to the Error enum in core/error/error_list.h
  * - Have a description added to error_names in core/error/error_list.cpp
  * - Have a description added to error_names in core/error/error_list.cpp
  * - Are bound with BIND_CORE_ENUM_CONSTANT() in core/core_constants.cpp
  * - Are bound with BIND_CORE_ENUM_CONSTANT() in core/core_constants.cpp
- * - Have a matching Android version in platform/android/java/lib/src/org/godotengine/godot/error/Error.kt
+ * - Have a matching Android version in platform/android/java/lib/src/main/java/org/godotengine/godot/error/Error.kt
  */
  */
 
 
 enum Error {
 enum Error {

+ 2 - 2
platform/android/export/export_plugin.cpp

@@ -221,7 +221,7 @@ static const char *MISMATCHED_VERSIONS_MESSAGE = "Android build version mismatch
 
 
 static const char *GDEXTENSION_LIBS_PATH = "libs/gdextensionlibs.json";
 static const char *GDEXTENSION_LIBS_PATH = "libs/gdextensionlibs.json";
 
 
-// This template string must be in sync with the content of 'platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml'.
+// This template string must be in sync with the content of 'platform/android/java/lib/src/main/java/res/mipmap-anydpi-v26/icon.xml'.
 static const String ICON_XML_TEMPLATE =
 static const String ICON_XML_TEMPLATE =
 		"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
 		"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
 		"<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n"
 		"<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n"
@@ -278,7 +278,7 @@ static const LauncherIcon LAUNCHER_ADAPTIVE_ICON_MONOCHROMES[ICON_DENSITIES_COUN
 static const int EXPORT_FORMAT_APK = 0;
 static const int EXPORT_FORMAT_APK = 0;
 static const int EXPORT_FORMAT_AAB = 1;
 static const int EXPORT_FORMAT_AAB = 1;
 
 
-static const char *APK_ASSETS_DIRECTORY = "assets";
+static const char *APK_ASSETS_DIRECTORY = "src/main/assets";
 static const char *AAB_ASSETS_DIRECTORY = "assetPackInstallTime/src/main/assets";
 static const char *AAB_ASSETS_DIRECTORY = "assetPackInstallTime/src/main/assets";
 
 
 static const int DEFAULT_MIN_SDK_VERSION = 24; // Should match the value in 'platform/android/java/app/config.gradle#minSdk'
 static const int DEFAULT_MIN_SDK_VERSION = 24; // Should match the value in 'platform/android/java/app/config.gradle#minSdk'

+ 1 - 1
platform/android/export/gradle_export_util.h

@@ -59,7 +59,7 @@ static const int APP_CATEGORY_VIDEO = 8;
 static const int APP_CATEGORY_UNDEFINED = 9;
 static const int APP_CATEGORY_UNDEFINED = 9;
 
 
 // Supported XR modes.
 // Supported XR modes.
-// This should match the entries in 'platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java'
+// This should match the entries in 'platform/android/java/lib/src/main/java/org/godotengine/godot/xr/XRMode.java'
 static const int XR_MODE_REGULAR = 0;
 static const int XR_MODE_REGULAR = 0;
 static const int XR_MODE_OPENXR = 1;
 static const int XR_MODE_OPENXR = 1;
 
 

+ 20 - 10
platform/android/java/app/build.gradle

@@ -34,6 +34,11 @@ configurations {
 }
 }
 
 
 dependencies {
 dependencies {
+    // Android instrumented test dependencies
+    androidTestImplementation "androidx.test.ext:junit:1.3.0"
+    androidTestImplementation "androidx.test.espresso:espresso-core:3.7.0"
+    androidTestImplementation "org.jetbrains.kotlin:kotlin-test:1.3.11"
+
     implementation "androidx.fragment:fragment:$versions.fragmentVersion"
     implementation "androidx.fragment:fragment:$versions.fragmentVersion"
     implementation "androidx.core:core-splashscreen:$versions.splashscreenVersion"
     implementation "androidx.core:core-splashscreen:$versions.splashscreenVersion"
 
 
@@ -114,6 +119,8 @@ android {
         targetSdkVersion getExportTargetSdkVersion()
         targetSdkVersion getExportTargetSdkVersion()
 
 
         missingDimensionStrategy 'products', 'template'
         missingDimensionStrategy 'products', 'template'
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
     }
     }
 
 
     lintOptions {
     lintOptions {
@@ -214,23 +221,26 @@ android {
     flavorDimensions 'edition'
     flavorDimensions 'edition'
 
 
     productFlavors {
     productFlavors {
+        // Product flavor for the standard (no .net support) builds.
         standard {
         standard {
             getIsDefault().set(true)
             getIsDefault().set(true)
         }
         }
+
+        // Product flavor for the Mono (.net) builds.
         mono {}
         mono {}
+
+        // Product flavor used for running instrumented tests.
+        instrumented {
+            applicationIdSuffix ".instrumented"
+            versionNameSuffix "-instrumented"
+        }
     }
     }
 
 
     sourceSets {
     sourceSets {
-        main {
-            manifest.srcFile 'AndroidManifest.xml'
-            java.srcDirs = ['src']
-            res.srcDirs = ['res']
-            aidl.srcDirs = ['aidl']
-            assets.srcDirs = ['assets']
-        }
-        debug.jniLibs.srcDirs = ['libs/debug', 'libs/debug/vulkan_validation_layers']
-        dev.jniLibs.srcDirs = ['libs/dev']
-        release.jniLibs.srcDirs = ['libs/release']
+        main.res.srcDirs += ['res']
+        debug.jniLibs.srcDirs += ['libs/debug', 'libs/debug/vulkan_validation_layers']
+        dev.jniLibs.srcDirs += ['libs/dev']
+        release.jniLibs.srcDirs += ['libs/release']
     }
     }
 
 
     applicationVariants.all { variant ->
     applicationVariants.all { variant ->

+ 76 - 0
platform/android/java/app/src/androidTestInstrumented/java/com/godot/game/GodotAppTest.kt

@@ -0,0 +1,76 @@
+/**************************************************************************/
+/*  GodotAppTest.kt                                                       */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+package com.godot.game
+
+import android.util.Log
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.godot.game.test.GodotAppInstrumentedTestPlugin
+import org.godotengine.godot.plugin.GodotPluginRegistry
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.test.assertNotNull
+import kotlin.test.assertTrue
+
+/**
+ * This instrumented test will launch the `instrumented` version of GodotApp and run a set of tests against it.
+ */
+@RunWith(AndroidJUnit4::class)
+class GodotAppTest {
+
+	companion object {
+		private val TAG = GodotAppTest::class.java.simpleName
+	}
+
+	@get:Rule
+	val godotAppRule = ActivityScenarioRule(GodotApp::class.java)
+
+	/**
+	 * Runs the JavaClassWrapper tests via the GodotAppInstrumentedTestPlugin.
+	 */
+	@Test
+	fun runJavaClassWrapperTests() {
+		val testPlugin = GodotPluginRegistry.getPluginRegistry()
+			.getPlugin("GodotAppInstrumentedTestPlugin") as GodotAppInstrumentedTestPlugin?
+		assertNotNull(testPlugin)
+
+		Log.d(TAG, "Waiting for the Godot main loop to start...")
+		testPlugin.waitForGodotMainLoopStarted()
+
+		Log.d(TAG, "Running JavaClassWrapper tests...")
+		val result = testPlugin.runJavaClassWrapperTests()
+		assertNotNull(result)
+		result.exceptionOrNull()?.let { throw it }
+		assertTrue(result.isSuccess)
+		Log.d(TAG, "Passed ${result.getOrNull()} tests")
+	}
+}

+ 9 - 0
platform/android/java/app/src/instrumented/AndroidManifest.xml

@@ -0,0 +1,9 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <application>
+        <meta-data
+            android:name="org.godotengine.plugin.v2.GodotAppInstrumentedTestPlugin"
+            android:value="com.godot.game.test.GodotAppInstrumentedTestPlugin"/>
+    </application>
+
+</manifest>

+ 2 - 0
platform/android/java/app/src/instrumented/assets/.gitattributes

@@ -0,0 +1,2 @@
+# Normalize EOL for all files that Git considers text files.
+* text=auto eol=lf

+ 3 - 0
platform/android/java/app/src/instrumented/assets/.gitignore

@@ -0,0 +1,3 @@
+# Godot 4+ specific ignores
+/android/
+/.godot/editor

+ 1 - 0
platform/android/java/app/src/instrumented/assets/.godot/.gdignore

@@ -0,0 +1 @@
+

+ 17 - 0
platform/android/java/app/src/instrumented/assets/.godot/global_script_class_cache.cfg

@@ -0,0 +1,17 @@
+list=[{
+"base": &"RefCounted",
+"class": &"BaseTest",
+"icon": "",
+"is_abstract": true,
+"is_tool": false,
+"language": &"GDScript",
+"path": "res://test/base_test.gd"
+}, {
+"base": &"BaseTest",
+"class": &"JavaClassWrapperTests",
+"icon": "",
+"is_abstract": false,
+"is_tool": false,
+"language": &"GDScript",
+"path": "res://test/javaclasswrapper/java_class_wrapper_tests.gd"
+}]

TEMPAT SAMPAH
platform/android/java/app/src/instrumented/assets/.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex


+ 2 - 0
platform/android/java/app/src/instrumented/assets/.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.md5

@@ -0,0 +1,2 @@
+source_md5="4cdc64b13a9af63279c486903c9b54cc"
+dest_md5="ddbdfc47e6405ad8d8e9e6a88a32824e"

+ 0 - 0
platform/android/java/app/src/instrumented/assets/.godot/scene_groups_cache.cfg


TEMPAT SAMPAH
platform/android/java/app/src/instrumented/assets/.godot/uid_cache.bin


+ 1 - 0
platform/android/java/app/src/instrumented/assets/icon.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 813 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H447l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c3 34 55 34 58 0v-86c-3-34-55-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>

+ 43 - 0
platform/android/java/app/src/instrumented/assets/icon.svg.import

@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://srnrli5m8won"
+path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://icon.svg"
+dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false

+ 60 - 0
platform/android/java/app/src/instrumented/assets/main.gd

@@ -0,0 +1,60 @@
+extends Node2D
+
+var _plugin_name = "GodotAppInstrumentedTestPlugin"
+var _android_plugin
+
+func _ready():
+	if Engine.has_singleton(_plugin_name):
+		_android_plugin = Engine.get_singleton(_plugin_name)
+		_android_plugin.connect("launch_tests", _launch_tests)
+	else:
+		printerr("Couldn't find plugin " + _plugin_name)
+		get_tree().quit()
+
+func _launch_tests(test_label: String) -> void:
+	var test_instance: BaseTest = null
+	match test_label:
+		"javaclasswrapper_tests":
+			test_instance = JavaClassWrapperTests.new()
+
+	if test_instance:
+		test_instance.__reset_tests()
+		test_instance.run_tests()
+		var incomplete_tests = test_instance._test_started - test_instance._test_completed
+		_android_plugin.onTestsCompleted(test_label, test_instance._test_completed, test_instance._test_assert_failures + incomplete_tests)
+	else:
+		_android_plugin.onTestsFailed(test_label, "Unable to launch tests")
+
+
+func _on_plugin_toast_button_pressed() -> void:
+	if _android_plugin:
+		_android_plugin.helloWorld()
+
+func _on_vibration_button_pressed() -> void:
+	var android_runtime = Engine.get_singleton("AndroidRuntime")
+	if android_runtime:
+		print("Checking if the device supports vibration")
+		var vibrator_service = android_runtime.getApplicationContext().getSystemService("vibrator")
+		if vibrator_service:
+			if vibrator_service.hasVibrator():
+				print("Vibration is supported on device! Vibrating now...")
+				var VibrationEffect = JavaClassWrapper.wrap("android.os.VibrationEffect")
+				var effect = VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE)
+				vibrator_service.vibrate(effect)
+			else:
+				printerr("Vibration is not supported on device")
+		else:
+			printerr("Unable to retrieve the vibrator service")
+	else:
+		printerr("Couldn't find AndroidRuntime singleton")
+
+func _on_gd_script_toast_button_pressed() -> void:
+	var android_runtime = Engine.get_singleton("AndroidRuntime")
+	if android_runtime:
+		var activity = android_runtime.getActivity()
+
+		var toastCallable = func ():
+			var ToastClass = JavaClassWrapper.wrap("android.widget.Toast")
+			ToastClass.makeText(activity, "Toast from GDScript", ToastClass.LENGTH_LONG).show()
+
+		activity.runOnUiThread(android_runtime.createRunnableFromGodotCallable(toastCallable))

+ 1 - 0
platform/android/java/app/src/instrumented/assets/main.gd.uid

@@ -0,0 +1 @@
+uid://bv6y7in6otgcm

+ 34 - 0
platform/android/java/app/src/instrumented/assets/main.tscn

@@ -0,0 +1,34 @@
+[gd_scene load_steps=2 format=3 uid="uid://cg3hylang5fxn"]
+
+[ext_resource type="Script" uid="uid://bv6y7in6otgcm" path="res://main.gd" id="1_j0gfq"]
+
+[node name="Main" type="Node2D"]
+script = ExtResource("1_j0gfq")
+
+[node name="VBoxContainer" type="VBoxContainer" parent="."]
+offset_left = 68.0
+offset_top = 102.0
+offset_right = 506.0
+offset_bottom = 408.0
+theme_override_constants/separation = 25
+
+[node name="PluginToastButton" type="Button" parent="VBoxContainer"]
+custom_minimum_size = Vector2(0, 50)
+layout_mode = 2
+text = "Plugin Toast
+"
+
+[node name="VibrationButton" type="Button" parent="VBoxContainer"]
+custom_minimum_size = Vector2(0, 50)
+layout_mode = 2
+text = "Vibration"
+
+[node name="GDScriptToastButton" type="Button" parent="VBoxContainer"]
+custom_minimum_size = Vector2(0, 50)
+layout_mode = 2
+text = "GDScript Toast
+"
+
+[connection signal="pressed" from="VBoxContainer/PluginToastButton" to="." method="_on_plugin_toast_button_pressed"]
+[connection signal="pressed" from="VBoxContainer/VibrationButton" to="." method="_on_vibration_button_pressed"]
+[connection signal="pressed" from="VBoxContainer/GDScriptToastButton" to="." method="_on_gd_script_toast_button_pressed"]

+ 26 - 0
platform/android/java/app/src/instrumented/assets/project.godot

@@ -0,0 +1,26 @@
+; Engine configuration file.
+; It's best edited using the editor UI and not directly,
+; since the parameters that go here are not all obvious.
+;
+; Format:
+;   [section] ; section goes between []
+;   param=value ; assign values to parameters
+
+config_version=5
+
+[application]
+
+config/name="Godot App Instrumentation Tests"
+run/main_scene="res://main.tscn"
+config/features=PackedStringArray("4.5", "GL Compatibility")
+config/icon="res://icon.svg"
+
+[debug]
+
+settings/stdout/verbose_stdout=true
+
+[rendering]
+
+renderer/rendering_method="gl_compatibility"
+renderer/rendering_method.mobile="gl_compatibility"
+textures/vram_compression/import_etc2_astc=true

+ 44 - 0
platform/android/java/app/src/instrumented/assets/test/base_test.gd

@@ -0,0 +1,44 @@
+@abstract class_name BaseTest
+
+var _test_started := 0
+var _test_completed := 0
+var _test_assert_passes := 0
+var _test_assert_failures := 0
+
+@abstract func run_tests()
+
+func __exec_test(test_func: Callable):
+	_test_started += 1
+	test_func.call()
+	_test_completed += 1
+
+func __reset_tests():
+	_test_started = 0
+	_test_completed = 0
+	_test_assert_passes = 0
+	_test_assert_failures = 0
+
+func __get_stack_frame():
+	for s in get_stack():
+		if not s.function.begins_with('__') and s.function != "assert_equal":
+			return s
+	return null
+
+func __assert_pass():
+	_test_assert_passes += 1
+	pass
+
+func __assert_fail():
+	_test_assert_failures += 1
+	var s = __get_stack_frame()
+	if s != null:
+		print_rich ("[color=red] == FAILURE: In function %s() from '%s' on line %s[/color]" % [s.function, s.source, s.line])
+	else:
+		print_rich ("[color=red] == FAILURE (run with --debug to get more information!) ==[/color]")
+
+func assert_equal(actual, expected):
+	if actual == expected:
+		__assert_pass()
+	else:
+		__assert_fail()
+		print ("    |-> Expected '%s' but got '%s'" % [expected, actual])

+ 1 - 0
platform/android/java/app/src/instrumented/assets/test/base_test.gd.uid

@@ -0,0 +1 @@
+uid://mofa8j0d801f

+ 136 - 0
platform/android/java/app/src/instrumented/assets/test/javaclasswrapper/java_class_wrapper_tests.gd

@@ -0,0 +1,136 @@
+class_name JavaClassWrapperTests
+extends BaseTest
+
+func run_tests():
+	print("JavaClassWrapper tests starting..")
+
+	__exec_test(test_exceptions)
+
+	__exec_test(test_multiple_signatures)
+	__exec_test(test_array_arguments)
+	__exec_test(test_array_return)
+
+	__exec_test(test_dictionary)
+
+	__exec_test(test_object_overload)
+
+	__exec_test(test_variant_conversion_safe_from_stack_overflow)
+
+	print("JavaClassWrapper tests finished.")
+	print("Tests started: " + str(_test_started))
+	print("Tests completed: " + str(_test_completed))
+
+
+func test_exceptions() -> void:
+	var TestClass: JavaClass = JavaClassWrapper.wrap('com.godot.game.test.javaclasswrapper.TestClass')
+	#print(TestClass.get_java_method_list())
+
+	assert_equal(JavaClassWrapper.get_exception(), null)
+
+	assert_equal(TestClass.testExc(27), 0)
+	assert_equal(str(JavaClassWrapper.get_exception()), '<JavaObject:java.lang.NullPointerException "java.lang.NullPointerException">')
+
+	assert_equal(JavaClassWrapper.get_exception(), null)
+
+func test_multiple_signatures() -> void:
+	var TestClass: JavaClass = JavaClassWrapper.wrap('com.godot.game.test.javaclasswrapper.TestClass')
+
+	var ai := [1, 2]
+	assert_equal(TestClass.testMethod(1, ai), "IntArray: [1, 2]")
+
+	var astr := ["abc"]
+	assert_equal(TestClass.testMethod(2, astr), "IntArray: [0]")
+
+	var atstr: Array[String] = ["abc"]
+	assert_equal(TestClass.testMethod(3, atstr), "StringArray: [abc]")
+
+	var TestClass2: JavaClass = JavaClassWrapper.wrap('com.godot.game.test.javaclasswrapper.TestClass2')
+	var aobjl: Array[Object] = [
+		TestClass2.TestClass2(27),
+		TestClass2.TestClass2(135),
+	]
+	assert_equal(TestClass.testMethod(3, aobjl), "testObjects: 27 135")
+
+func test_array_arguments() -> void:
+	var TestClass: JavaClass = JavaClassWrapper.wrap('com.godot.game.test.javaclasswrapper.TestClass')
+
+	assert_equal(TestClass.testArgBoolArray([true, false, true]), "[true, false, true]")
+	assert_equal(TestClass.testArgByteArray(PackedByteArray([1, 2, 3])), "[1, 2, 3]")
+	assert_equal(TestClass.testArgCharArray("abc".to_utf16_buffer()), "abc");
+	assert_equal(TestClass.testArgShortArray(PackedInt32Array([27, 28, 29])), "[27, 28, 29]")
+	assert_equal(TestClass.testArgShortArray([27, 28, 29]), "[27, 28, 29]")
+	assert_equal(TestClass.testArgIntArray(PackedInt32Array([7, 8, 9])), "[7, 8, 9]")
+	assert_equal(TestClass.testArgIntArray([7, 8, 9]), "[7, 8, 9]")
+	assert_equal(TestClass.testArgLongArray(PackedInt64Array([17, 18, 19])), "[17, 18, 19]")
+	assert_equal(TestClass.testArgLongArray([17, 18, 19]), "[17, 18, 19]")
+	assert_equal(TestClass.testArgFloatArray(PackedFloat32Array([17.1, 18.2, 19.3])), "[17.1, 18.2, 19.3]")
+	assert_equal(TestClass.testArgFloatArray([17.1, 18.2, 19.3]), "[17.1, 18.2, 19.3]")
+	assert_equal(TestClass.testArgDoubleArray(PackedFloat64Array([37.1, 38.2, 39.3])), "[37.1, 38.2, 39.3]")
+	assert_equal(TestClass.testArgDoubleArray([37.1, 38.2, 39.3]), "[37.1, 38.2, 39.3]")
+
+func test_array_return() -> void:
+	var TestClass: JavaClass = JavaClassWrapper.wrap('com.godot.game.test.javaclasswrapper.TestClass')
+	#print(TestClass.get_java_method_list())
+
+	assert_equal(TestClass.testRetBoolArray(), [true, false, true])
+	assert_equal(TestClass.testRetWrappedBoolArray(), [true, false, true])
+
+	assert_equal(TestClass.testRetByteArray(), PackedByteArray([1, 2, 3]))
+	assert_equal(TestClass.testRetWrappedByteArray(), PackedByteArray([1, 2, 3]))
+
+	assert_equal(TestClass.testRetCharArray().get_string_from_utf16(), "abc")
+	assert_equal(TestClass.testRetWrappedCharArray().get_string_from_utf16(), "abc")
+
+	assert_equal(TestClass.testRetShortArray(), PackedInt32Array([11, 12, 13]))
+	assert_equal(TestClass.testRetWrappedShortArray(), PackedInt32Array([11, 12, 13]))
+
+	assert_equal(TestClass.testRetIntArray(), PackedInt32Array([21, 22, 23]))
+	assert_equal(TestClass.testRetWrappedIntArray(), PackedInt32Array([21, 22, 23]))
+
+	assert_equal(TestClass.testRetLongArray(), PackedInt64Array([41, 42, 43]))
+	assert_equal(TestClass.testRetWrappedLongArray(), PackedInt64Array([41, 42, 43]))
+
+	assert_equal(TestClass.testRetFloatArray(), PackedFloat32Array([31.1, 32.2, 33.3]))
+	assert_equal(TestClass.testRetWrappedFloatArray(), PackedFloat32Array([31.1, 32.2, 33.3]))
+
+	assert_equal(TestClass.testRetDoubleArray(), PackedFloat64Array([41.1, 42.2, 43.3]))
+	assert_equal(TestClass.testRetWrappedDoubleArray(), PackedFloat64Array([41.1, 42.2, 43.3]))
+
+	var obj_array = TestClass.testRetObjectArray()
+	assert_equal(str(obj_array[0]), '<JavaObject:com.godot.game.test.javaclasswrapper.TestClass2 "51">')
+	assert_equal(str(obj_array[1]), '<JavaObject:com.godot.game.test.javaclasswrapper.TestClass2 "52">')
+
+	assert_equal(TestClass.testRetStringArray(), PackedStringArray(["I", "am", "String"]))
+	assert_equal(TestClass.testRetCharSequenceArray(), PackedStringArray(["I", "am", "CharSequence"]))
+
+func test_dictionary():
+	var TestClass: JavaClass = JavaClassWrapper.wrap('com.godot.game.test.javaclasswrapper.TestClass')
+	assert_equal(TestClass.testDictionary({a = 1, b = 2}), "{a=1, b=2}")
+	assert_equal(TestClass.testRetDictionary(), {a = 1, b = 2})
+	assert_equal(TestClass.testRetDictionaryArray(), [{a = 1, b = 2}])
+	assert_equal(TestClass.testDictionaryNested({a = 1, b = [2, 3], c = 4}), "{a: 1, b: [2, 3], c: 4}")
+
+func test_object_overload():
+	var TestClass: JavaClass = JavaClassWrapper.wrap('com.godot.game.test.javaclasswrapper.TestClass')
+	var TestClass2: JavaClass = JavaClassWrapper.wrap('com.godot.game.test.javaclasswrapper.TestClass2')
+	var TestClass3: JavaClass = JavaClassWrapper.wrap('com.godot.game.test.javaclasswrapper.TestClass3')
+
+	var t2 = TestClass2.TestClass2(33)
+	var t3 = TestClass3.TestClass3("thirty three")
+
+	assert_equal(TestClass.testObjectOverload(t2), "TestClass2: 33")
+	assert_equal(TestClass.testObjectOverload(t3), "TestClass3: thirty three")
+
+	var arr_of_t2 = [t2, TestClass2.TestClass2(34)]
+	var arr_of_t3 = [t3, TestClass3.TestClass3("thirty four")]
+
+	assert_equal(TestClass.testObjectOverloadArray(arr_of_t2), "TestClass2: [33, 34]")
+	assert_equal(TestClass.testObjectOverloadArray(arr_of_t3), "TestClass3: [thirty three, thirty four]")
+
+func test_variant_conversion_safe_from_stack_overflow():
+	var TestClass: JavaClass = JavaClassWrapper.wrap('com.godot.game.test.javaclasswrapper.TestClass')
+	var arr: Array = [42]
+	var dict: Dictionary = {"arr": arr}
+	arr.append(dict)
+	# The following line will crash with stack overflow if not handled property:
+	TestClass.testDictionary(dict)

+ 1 - 0
platform/android/java/app/src/instrumented/assets/test/javaclasswrapper/java_class_wrapper_tests.gd.uid

@@ -0,0 +1 @@
+uid://3ql82ggk41xc

+ 144 - 0
platform/android/java/app/src/instrumented/java/com/godot/game/test/GodotAppInstrumentedTestPlugin.kt

@@ -0,0 +1,144 @@
+/**************************************************************************/
+/*  GodotAppInstrumentedTestPlugin.kt                                     */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+package com.godot.game.test
+
+import android.util.Log
+import android.widget.Toast
+import org.godotengine.godot.Godot
+import org.godotengine.godot.plugin.GodotPlugin
+import org.godotengine.godot.plugin.UsedByGodot
+import org.godotengine.godot.plugin.SignalInfo
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.CountDownLatch
+
+/**
+ * [GodotPlugin] used to drive instrumented tests.
+ */
+class GodotAppInstrumentedTestPlugin(godot: Godot) : GodotPlugin(godot) {
+
+	companion object {
+		private val TAG = GodotAppInstrumentedTestPlugin::class.java.simpleName
+		private const val MAIN_LOOP_STARTED_LATCH_KEY = "main_loop_started_latch"
+
+		private const val JAVACLASSWRAPPER_TESTS = "javaclasswrapper_tests"
+
+		private val LAUNCH_TESTS_SIGNAL = SignalInfo("launch_tests", String::class.java)
+
+		private val SIGNALS = setOf(
+			LAUNCH_TESTS_SIGNAL
+		)
+	}
+
+	private val testResults = ConcurrentHashMap<String, Result<Any>>()
+	private val latches = ConcurrentHashMap<String, CountDownLatch>()
+
+	init {
+		// Add a countdown latch that is triggered when `onGodotMainLoopStarted` is fired.
+		// This will be used by tests to wait until the engine is ready.
+		latches[MAIN_LOOP_STARTED_LATCH_KEY] = CountDownLatch(1)
+	}
+
+	override fun getPluginName() = "GodotAppInstrumentedTestPlugin"
+
+	override fun getPluginSignals() = SIGNALS
+
+	override fun onGodotMainLoopStarted() {
+		super.onGodotMainLoopStarted()
+		latches.remove(MAIN_LOOP_STARTED_LATCH_KEY)?.countDown()
+	}
+
+	/**
+	 * Used by the instrumented test to wait until the Godot main loop is up and running.
+	 */
+	internal fun waitForGodotMainLoopStarted() {
+		// Wait on the CountDownLatch for `onGodotMainLoopStarted`
+		try {
+			latches[MAIN_LOOP_STARTED_LATCH_KEY]?.await()
+		} catch (e: InterruptedException) {
+			Log.e(TAG, "Unable to wait for Godot main loop started event.", e)
+		}
+	}
+
+	/**
+	 * This launches the JavaClassWrapper tests, and wait until the tests are complete before returning.
+	 */
+	internal fun runJavaClassWrapperTests(): Result<Any>? {
+		return launchTests(JAVACLASSWRAPPER_TESTS)
+	}
+
+	private fun launchTests(testLabel: String): Result<Any>? {
+		val latch = latches.getOrPut(testLabel) { CountDownLatch(1) }
+		emitSignal(LAUNCH_TESTS_SIGNAL.name, testLabel)
+		return try {
+			latch.await()
+			val result = testResults.remove(testLabel)
+			result
+		} catch (e: InterruptedException) {
+			Log.e(TAG, "Unable to wait for completion for $testLabel", e)
+			null
+		}
+	}
+
+	/**
+	 * Callback invoked from gdscript when the tests are completed.
+	 */
+	@UsedByGodot
+	fun onTestsCompleted(testLabel: String, passes: Int, failures: Int) {
+		Log.d(TAG, "$testLabel tests completed")
+		val result = if (failures == 0) {
+			Result.success(passes)
+		} else {
+			Result.failure(AssertionError("$failures tests failed!"))
+		}
+
+		completeTest(testLabel, result)
+	}
+
+	@UsedByGodot
+	fun onTestsFailed(testLabel: String, failureMessage: String) {
+		Log.d(TAG, "$testLabel tests failed")
+		val result: Result<Any> = Result.failure(AssertionError(failureMessage))
+		completeTest(testLabel, result)
+	}
+
+	private fun completeTest(testKey: String, result: Result<Any>) {
+		testResults[testKey] = result
+		latches.remove(testKey)?.countDown()
+	}
+
+	@UsedByGodot
+	fun helloWorld() {
+		runOnHostThread {
+			Toast.makeText(activity, "Toast from Android plugin", Toast.LENGTH_LONG).show()
+			Log.v(pluginName, "Hello World")
+		}
+	}
+}

+ 261 - 0
platform/android/java/app/src/instrumented/java/com/godot/game/test/javaclasswrapper/TestClass.kt

@@ -0,0 +1,261 @@
+/**************************************************************************/
+/*  TestClass.kt                                                          */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+package com.godot.game.test.javaclasswrapper
+
+import org.godotengine.godot.Dictionary
+import kotlin.collections.contentToString
+import kotlin.collections.joinToString
+
+class TestClass {
+	companion object {
+		@JvmStatic
+		fun stringify(value: Any?): String {
+			return when (value) {
+				null -> "null"
+				is Map<*, *> -> {
+					val entries = value.entries.joinToString(", ") { (k, v) -> "${stringify(k)}: ${stringify(v)}" }
+					"{$entries}"
+				}
+
+				is List<*> -> value.joinToString(prefix = "[", postfix = "]") { stringify(it) }
+				is Array<*> -> value.joinToString(prefix = "[", postfix = "]") { stringify(it) }
+				is IntArray -> value.joinToString(prefix = "[", postfix = "]")
+				is LongArray -> value.joinToString(prefix = "[", postfix = "]")
+				is FloatArray -> value.joinToString(prefix = "[", postfix = "]")
+				is DoubleArray -> value.joinToString(prefix = "[", postfix = "]")
+				is BooleanArray -> value.joinToString(prefix = "[", postfix = "]")
+				is CharArray -> value.joinToString(prefix = "[", postfix = "]")
+				else -> value.toString()
+			}
+		}
+
+		@JvmStatic
+		fun testDictionary(d: Dictionary): String {
+			return d.toString()
+		}
+
+		@JvmStatic
+		fun testDictionaryNested(d: Dictionary): String {
+			return stringify(d)
+		}
+
+		@JvmStatic
+		fun testRetDictionary(): Dictionary {
+			var d = Dictionary()
+			d.putAll(mapOf("a" to 1, "b" to 2))
+			return d
+		}
+
+		@JvmStatic
+		fun testRetDictionaryArray(): Array<Dictionary> {
+			var d = Dictionary()
+			d.putAll(mapOf("a" to 1, "b" to 2))
+			return arrayOf(d)
+		}
+
+		@JvmStatic
+		fun testMethod(int: Int, array: IntArray): String {
+			return "IntArray: " + array.contentToString()
+		}
+
+		@JvmStatic
+		fun testMethod(int: Int, vararg args: String): String {
+			return "StringArray: " + args.contentToString()
+		}
+
+		@JvmStatic
+		fun testMethod(int: Int, objects: Array<TestClass2>): String {
+			return "testObjects: " + objects.joinToString(separator = " ") { it.getValue().toString() }
+		}
+
+		@JvmStatic
+		fun testExc(i: Int): Int {
+			val s: String? = null
+			s!!.length
+			return i
+		}
+
+		@JvmStatic
+		fun testArgBoolArray(a: BooleanArray): String {
+			return a.contentToString();
+		}
+
+		@JvmStatic
+		fun testArgByteArray(a: ByteArray): String {
+			return a.contentToString();
+		}
+
+		@JvmStatic
+		fun testArgCharArray(a: CharArray): String {
+			return a.joinToString("")
+		}
+
+		@JvmStatic
+		fun testArgShortArray(a: ShortArray): String {
+			return a.contentToString();
+		}
+
+		@JvmStatic
+		fun testArgIntArray(a: IntArray): String {
+			return a.contentToString();
+		}
+
+		@JvmStatic
+		fun testArgLongArray(a: LongArray): String {
+			return a.contentToString();
+		}
+
+		@JvmStatic
+		fun testArgFloatArray(a: FloatArray): String {
+			return a.contentToString();
+		}
+
+		@JvmStatic
+		fun testArgDoubleArray(a: DoubleArray): String {
+			return a.contentToString();
+		}
+
+		@JvmStatic
+		fun testRetBoolArray(): BooleanArray {
+			return booleanArrayOf(true, false, true)
+		}
+
+		@JvmStatic
+		fun testRetByteArray(): ByteArray {
+			return byteArrayOf(1, 2, 3)
+		}
+
+		@JvmStatic
+		fun testRetCharArray(): CharArray {
+			return "abc".toCharArray()
+		}
+
+		@JvmStatic
+		fun testRetShortArray(): ShortArray {
+			return shortArrayOf(11, 12, 13)
+		}
+
+		@JvmStatic
+		fun testRetIntArray(): IntArray {
+			return intArrayOf(21, 22, 23)
+		}
+
+		@JvmStatic
+		fun testRetLongArray(): LongArray {
+			return longArrayOf(41, 42, 43)
+		}
+
+		@JvmStatic
+		fun testRetFloatArray(): FloatArray {
+			return floatArrayOf(31.1f, 32.2f, 33.3f)
+		}
+
+		@JvmStatic
+		fun testRetDoubleArray(): DoubleArray {
+			return doubleArrayOf(41.1, 42.2, 43.3)
+		}
+
+		@JvmStatic
+		fun testRetWrappedBoolArray(): Array<Boolean> {
+			return arrayOf(true, false, true)
+		}
+
+		@JvmStatic
+		fun testRetWrappedByteArray(): Array<Byte> {
+			return arrayOf(1, 2, 3)
+		}
+
+		@JvmStatic
+		fun testRetWrappedCharArray(): Array<Char> {
+			return arrayOf('a', 'b', 'c')
+		}
+
+		@JvmStatic
+		fun testRetWrappedShortArray(): Array<Short> {
+			return arrayOf(11, 12, 13)
+		}
+
+		@JvmStatic
+		fun testRetWrappedIntArray(): Array<Int> {
+			return arrayOf(21, 22, 23)
+		}
+
+		@JvmStatic
+		fun testRetWrappedLongArray(): Array<Long> {
+			return arrayOf(41, 42, 43)
+		}
+
+		@JvmStatic
+		fun testRetWrappedFloatArray(): Array<Float> {
+			return arrayOf(31.1f, 32.2f, 33.3f)
+		}
+
+		@JvmStatic
+		fun testRetWrappedDoubleArray(): Array<Double> {
+			return arrayOf(41.1, 42.2, 43.3)
+		}
+
+		@JvmStatic
+		fun testRetObjectArray(): Array<TestClass2> {
+			return arrayOf(TestClass2(51), TestClass2(52));
+		}
+
+		@JvmStatic
+		fun testRetStringArray(): Array<String> {
+			return arrayOf("I", "am", "String")
+		}
+
+		@JvmStatic
+		fun testRetCharSequenceArray(): Array<CharSequence> {
+			return arrayOf("I", "am", "CharSequence")
+		}
+
+		@JvmStatic
+		fun testObjectOverload(a: TestClass2): String {
+			return "TestClass2: $a"
+		}
+
+		@JvmStatic
+		fun testObjectOverload(a: TestClass3): String {
+			return "TestClass3: $a"
+		}
+
+		@JvmStatic
+		fun testObjectOverloadArray(a: Array<TestClass2>): String {
+			return "TestClass2: " + a.contentToString()
+		}
+
+		@JvmStatic
+		fun testObjectOverloadArray(a: Array<TestClass3>): String {
+			return "TestClass3: " + a.contentToString()
+		}
+	}
+}

+ 40 - 0
platform/android/java/app/src/instrumented/java/com/godot/game/test/javaclasswrapper/TestClass2.kt

@@ -0,0 +1,40 @@
+/**************************************************************************/
+/*  TestClass2.kt                                                         */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+package com.godot.game.test.javaclasswrapper
+
+class TestClass2(private val value: Int) {
+    fun getValue(): Int {
+        return value
+    }
+    override fun toString(): String {
+        return value.toString()
+    }
+}

+ 40 - 0
platform/android/java/app/src/instrumented/java/com/godot/game/test/javaclasswrapper/TestClass3.kt

@@ -0,0 +1,40 @@
+/**************************************************************************/
+/*  TestClass3.kt                                                         */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+package com.godot.game.test.javaclasswrapper
+
+class TestClass3(private val value: String) {
+    fun getValue(): String {
+        return value
+    }
+    override fun toString(): String {
+        return value
+    }
+}

+ 4 - 0
platform/android/java/app/src/instrumented/res/values/strings.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+	<string name="godot_project_name_string">Godot App Instrumented Tests</string>
+</resources>

+ 0 - 0
platform/android/java/app/AndroidManifest.xml → platform/android/java/app/src/main/AndroidManifest.xml


+ 0 - 0
platform/android/java/app/assets/.gitignore → platform/android/java/app/src/main/assets/.gitignore


+ 9 - 0
platform/android/java/app/src/com/godot/game/GodotApp.java → platform/android/java/app/src/main/java/com/godot/game/GodotApp.java

@@ -83,4 +83,13 @@ public class GodotApp extends GodotActivity {
 		super.onGodotMainLoopStarted();
 		super.onGodotMainLoopStarted();
 		runOnUiThread(updateWindowAppearance);
 		runOnUiThread(updateWindowAppearance);
 	}
 	}
+
+	@Override
+	public void onGodotForceQuit(Godot instance) {
+		if (!BuildConfig.FLAVOR.equals("instrumented")) {
+			// For instrumented builds, we disable force-quitting to allow the instrumented tests to complete
+			// successfully, otherwise they fail when the process crashes.
+			super.onGodotForceQuit(instance);
+		}
+	}
 }
 }

+ 6 - 15
platform/android/java/lib/build.gradle

@@ -77,23 +77,14 @@ android {
     }
     }
 
 
     sourceSets {
     sourceSets {
-        main {
-            manifest.srcFile 'AndroidManifest.xml'
-            java.srcDirs = ['src']
-            test.java.srcDirs = ['srcTest/java']
-            res.srcDirs = ['res']
-            aidl.srcDirs = ['aidl']
-            assets.srcDirs = ['assets']
-        }
-
-        debug.jniLibs.srcDirs = ['libs/debug']
-        dev.jniLibs.srcDirs = ['libs/dev']
-        release.jniLibs.srcDirs = ['libs/release']
+        debug.jniLibs.srcDirs += ['libs/debug']
+        dev.jniLibs.srcDirs += ['libs/dev']
+        release.jniLibs.srcDirs += ['libs/release']
 
 
         // Editor jni library
         // Editor jni library
-        editorRelease.jniLibs.srcDirs = ['libs/tools/release']
-        editorDebug.jniLibs.srcDirs = ['libs/tools/debug']
-        editorDev.jniLibs.srcDirs = ['libs/tools/dev']
+        editorRelease.jniLibs.srcDirs += ['libs/tools/release']
+        editorDebug.jniLibs.srcDirs += ['libs/tools/debug']
+        editorDev.jniLibs.srcDirs += ['libs/tools/dev']
     }
     }
 
 
     libraryVariants.all { variant ->
     libraryVariants.all { variant ->

+ 0 - 0
platform/android/java/lib/AndroidManifest.xml → platform/android/java/lib/src/main/AndroidManifest.xml


+ 0 - 0
platform/android/java/lib/aidl/com/android/vending/licensing/ILicenseResultListener.aidl → platform/android/java/lib/src/main/aidl/com/android/vending/licensing/ILicenseResultListener.aidl


+ 0 - 0
platform/android/java/lib/aidl/com/android/vending/licensing/ILicensingService.aidl → platform/android/java/lib/src/main/aidl/com/android/vending/licensing/ILicensingService.aidl


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/expansion/downloader/Constants.java → platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/Constants.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/expansion/downloader/DownloadProgressInfo.java → platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/DownloadProgressInfo.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/expansion/downloader/DownloaderClientMarshaller.java → platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/DownloaderClientMarshaller.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/expansion/downloader/DownloaderServiceMarshaller.java → platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/DownloaderServiceMarshaller.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/expansion/downloader/Helpers.java → platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/Helpers.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/expansion/downloader/IDownloaderClient.java → platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/IDownloaderClient.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/expansion/downloader/IDownloaderService.java → platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/IDownloaderService.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/expansion/downloader/IStub.java → platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/IStub.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/expansion/downloader/SystemFacade.java → platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/SystemFacade.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/expansion/downloader/impl/CustomIntentService.java → platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/impl/CustomIntentService.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/expansion/downloader/impl/DownloadInfo.java → platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/impl/DownloadInfo.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java → platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java


+ 1 - 1
platform/android/java/lib/src/com/google/android/vending/expansion/downloader/impl/DownloadThread.java → platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/impl/DownloadThread.java

@@ -110,7 +110,7 @@ public class DownloadThread {
      * headers, or destination filename.
      * headers, or destination filename.
      */
      */
     private class StopRequest extends Throwable {
     private class StopRequest extends Throwable {
-    	
+
         private static final long serialVersionUID = 6338592678988347973L;
         private static final long serialVersionUID = 6338592678988347973L;
         public int mFinalStatus;
         public int mFinalStatus;
 
 

+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/expansion/downloader/impl/DownloaderService.java → platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/impl/DownloaderService.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/expansion/downloader/impl/DownloadsDB.java → platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/impl/DownloadsDB.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/expansion/downloader/impl/HttpDateTime.java → platform/android/java/lib/src/main/java/com/google/android/vending/expansion/downloader/impl/HttpDateTime.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/licensing/AESObfuscator.java → platform/android/java/lib/src/main/java/com/google/android/vending/licensing/AESObfuscator.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/licensing/APKExpansionPolicy.java → platform/android/java/lib/src/main/java/com/google/android/vending/licensing/APKExpansionPolicy.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/licensing/DeviceLimiter.java → platform/android/java/lib/src/main/java/com/google/android/vending/licensing/DeviceLimiter.java


+ 1 - 1
platform/android/java/lib/src/com/google/android/vending/licensing/LicenseChecker.java → platform/android/java/lib/src/main/java/com/google/android/vending/licensing/LicenseChecker.java

@@ -133,7 +133,7 @@ public class LicenseChecker implements ServiceConnection {
      * <p>
      * <p>
      * source string: "com.android.vending.licensing.ILicensingService"
      * source string: "com.android.vending.licensing.ILicensingService"
      * <p>
      * <p>
-     * 
+     *
      * @param callback
      * @param callback
      */
      */
     public synchronized void checkAccess(LicenseCheckerCallback callback) {
     public synchronized void checkAccess(LicenseCheckerCallback callback) {

+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/licensing/LicenseCheckerCallback.java → platform/android/java/lib/src/main/java/com/google/android/vending/licensing/LicenseCheckerCallback.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/licensing/LicenseValidator.java → platform/android/java/lib/src/main/java/com/google/android/vending/licensing/LicenseValidator.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/licensing/NullDeviceLimiter.java → platform/android/java/lib/src/main/java/com/google/android/vending/licensing/NullDeviceLimiter.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/licensing/Obfuscator.java → platform/android/java/lib/src/main/java/com/google/android/vending/licensing/Obfuscator.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/licensing/Policy.java → platform/android/java/lib/src/main/java/com/google/android/vending/licensing/Policy.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/licensing/PreferenceObfuscator.java → platform/android/java/lib/src/main/java/com/google/android/vending/licensing/PreferenceObfuscator.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/licensing/ResponseData.java → platform/android/java/lib/src/main/java/com/google/android/vending/licensing/ResponseData.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/licensing/ServerManagedPolicy.java → platform/android/java/lib/src/main/java/com/google/android/vending/licensing/ServerManagedPolicy.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/licensing/StrictPolicy.java → platform/android/java/lib/src/main/java/com/google/android/vending/licensing/StrictPolicy.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/licensing/ValidationException.java → platform/android/java/lib/src/main/java/com/google/android/vending/licensing/ValidationException.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/licensing/util/Base64.java → platform/android/java/lib/src/main/java/com/google/android/vending/licensing/util/Base64.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/licensing/util/Base64DecoderException.java → platform/android/java/lib/src/main/java/com/google/android/vending/licensing/util/Base64DecoderException.java


+ 0 - 0
platform/android/java/lib/src/com/google/android/vending/licensing/util/URIQueryDecoder.java → platform/android/java/lib/src/main/java/com/google/android/vending/licensing/util/URIQueryDecoder.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/Dictionary.java → platform/android/java/lib/src/main/java/org/godotengine/godot/Dictionary.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/FullScreenGodotApp.java → platform/android/java/lib/src/main/java/org/godotengine/godot/FullScreenGodotApp.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/Godot.kt → platform/android/java/lib/src/main/java/org/godotengine/godot/Godot.kt


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/GodotActivity.kt → platform/android/java/lib/src/main/java/org/godotengine/godot/GodotActivity.kt


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java → platform/android/java/lib/src/main/java/org/godotengine/godot/GodotDownloaderAlarmReceiver.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/GodotDownloaderService.java → platform/android/java/lib/src/main/java/org/godotengine/godot/GodotDownloaderService.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java → platform/android/java/lib/src/main/java/org/godotengine/godot/GodotFragment.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java → platform/android/java/lib/src/main/java/org/godotengine/godot/GodotGLRenderView.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/GodotHost.java → platform/android/java/lib/src/main/java/org/godotengine/godot/GodotHost.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/GodotIO.java → platform/android/java/lib/src/main/java/org/godotengine/godot/GodotIO.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/GodotLib.java → platform/android/java/lib/src/main/java/org/godotengine/godot/GodotLib.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java → platform/android/java/lib/src/main/java/org/godotengine/godot/GodotRenderView.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java → platform/android/java/lib/src/main/java/org/godotengine/godot/GodotVulkanRenderView.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/editor/utils/EditorUtils.kt → platform/android/java/lib/src/main/java/org/godotengine/godot/editor/utils/EditorUtils.kt


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/editor/utils/GameMenuUtils.kt → platform/android/java/lib/src/main/java/org/godotengine/godot/editor/utils/GameMenuUtils.kt


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/error/Error.kt → platform/android/java/lib/src/main/java/org/godotengine/godot/error/Error.kt


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper.java → platform/android/java/lib/src/main/java/org/godotengine/godot/gl/EGLLogWrapper.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java → platform/android/java/lib/src/main/java/org/godotengine/godot/gl/GLSurfaceView.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/gl/GodotRenderer.java → platform/android/java/lib/src/main/java/org/godotengine/godot/gl/GodotRenderer.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java → platform/android/java/lib/src/main/java/org/godotengine/godot/input/GodotEditText.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt → platform/android/java/lib/src/main/java/org/godotengine/godot/input/GodotGestureHandler.kt


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java → platform/android/java/lib/src/main/java/org/godotengine/godot/input/GodotInputHandler.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java → platform/android/java/lib/src/main/java/org/godotengine/godot/input/GodotTextInputWrapper.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/input/InputEventRunnable.java → platform/android/java/lib/src/main/java/org/godotengine/godot/input/InputEventRunnable.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java → platform/android/java/lib/src/main/java/org/godotengine/godot/input/Joystick.java


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/io/FilePicker.kt → platform/android/java/lib/src/main/java/org/godotengine/godot/io/FilePicker.kt


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/io/StorageScope.kt → platform/android/java/lib/src/main/java/org/godotengine/godot/io/StorageScope.kt


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/io/directory/AssetsDirectoryAccess.kt → platform/android/java/lib/src/main/java/org/godotengine/godot/io/directory/AssetsDirectoryAccess.kt


+ 0 - 0
platform/android/java/lib/src/org/godotengine/godot/io/directory/DirectoryAccessHandler.kt → platform/android/java/lib/src/main/java/org/godotengine/godot/io/directory/DirectoryAccessHandler.kt


Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini