summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--editor/editor_node.cpp3
-rw-r--r--main/main.cpp3
-rw-r--r--platform/android/SCsub11
-rw-r--r--platform/android/java/app/build.gradle34
-rw-r--r--platform/android/java/app/config.gradle52
-rw-r--r--platform/android/java/build.gradle144
-rw-r--r--platform/android/java/editor/build.gradle74
-rw-r--r--platform/android/java/editor/src/dev/res/values/strings.xml4
-rw-r--r--platform/android/java/editor/src/main/AndroidManifest.xml67
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.java110
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.java37
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.java41
-rw-r--r--platform/android/java/editor/src/main/res/values/strings.xml4
-rw-r--r--platform/android/java/lib/build.gradle84
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java27
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotHost.java10
-rw-r--r--platform/android/java/nativeSrcsConfigs/CMakeLists.txt2
-rw-r--r--platform/android/java/nativeSrcsConfigs/build.gradle3
-rw-r--r--platform/android/java/scripts/publish-module.gradle11
-rw-r--r--platform/android/java/settings.gradle1
-rw-r--r--platform/android/java_godot_lib_jni.cpp6
-rw-r--r--platform/android/java_godot_wrapper.cpp14
-rw-r--r--platform/android/java_godot_wrapper.h3
-rw-r--r--platform/android/os_android.cpp54
-rw-r--r--platform/android/os_android.h9
25 files changed, 740 insertions, 68 deletions
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 92ab20b999..57998b7778 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -635,6 +635,9 @@ void EditorNode::_notification(int p_what) {
get_tree()->get_root()->set_snap_2d_transforms_to_pixel(false);
get_tree()->get_root()->set_snap_2d_vertices_to_pixel(false);
get_tree()->set_auto_accept_quit(false);
+#ifdef ANDROID_ENABLED
+ get_tree()->set_quit_on_go_back(false);
+#endif
get_tree()->get_root()->connect("files_dropped", callable_mp(this, &EditorNode::_dropped_files));
command_palette->register_shortcuts_as_command();
diff --git a/main/main.cpp b/main/main.cpp
index f41fa136ba..c7a644d7b3 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -2276,9 +2276,10 @@ bool Main::start() {
bool embed_subwindows = GLOBAL_DEF("display/window/subwindows/embed_subwindows", true);
- if (OS::get_singleton()->is_single_window() || (!project_manager && !editor && embed_subwindows)) {
+ if (OS::get_singleton()->is_single_window() || (!project_manager && !editor && embed_subwindows) || !DisplayServer::get_singleton()->has_feature(DisplayServer::Feature::FEATURE_SUBWINDOWS)) {
sml->get_root()->set_embedding_subwindows(true);
}
+
ResourceLoader::add_custom_loaders();
ResourceSaver::add_custom_savers();
diff --git a/platform/android/SCsub b/platform/android/SCsub
index d031d14499..1a3c158d2e 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -53,10 +53,17 @@ else:
if lib_arch_dir != "":
if env["target"] == "release":
lib_type_dir = "release"
- else: # release_debug, debug
+ elif env["target"] == "release_debug":
lib_type_dir = "debug"
+ else: # debug
+ lib_type_dir = "dev"
- out_dir = "#platform/android/java/lib/libs/" + lib_type_dir + "/" + lib_arch_dir
+ if env["tools"]:
+ lib_tools_dir = "tools/"
+ else:
+ lib_tools_dir = ""
+
+ out_dir = "#platform/android/java/lib/libs/" + lib_tools_dir + lib_type_dir + "/" + lib_arch_dir
env_android.Command(
out_dir + "/libgodot_android.so", "#bin/libgodot" + env["SHLIBSUFFIX"], Move("$TARGET", "$SOURCE")
)
diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle
index 5d1a9d7b99..b6303d1bc9 100644
--- a/platform/android/java/app/build.gradle
+++ b/platform/android/java/app/build.gradle
@@ -33,6 +33,11 @@ allprojects {
}
}
+configurations {
+ // Initializes a placeholder for the devImplementation dependency configuration.
+ devImplementation {}
+}
+
dependencies {
implementation libraries.kotlinStdLib
implementation libraries.androidxFragment
@@ -45,6 +50,7 @@ dependencies {
// Custom build mode. In this scenario this project is the only one around and the Godot
// library is available through the pre-generated godot-lib.*.aar android archive files.
debugImplementation fileTree(dir: 'libs/debug', include: ['*.jar', '*.aar'])
+ devImplementation fileTree(dir: 'libs/dev', include: ['*.jar', '*.aar'])
releaseImplementation fileTree(dir: 'libs/release', include: ['*.jar', '*.aar'])
}
@@ -66,6 +72,7 @@ dependencies {
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
+ ndkVersion versions.ndkVersion
compileOptions {
sourceCompatibility versions.javaVersion
@@ -93,6 +100,8 @@ android {
versionName getExportVersionName()
minSdkVersion getExportMinSdkVersion()
targetSdkVersion getExportTargetSdkVersion()
+
+ missingDimensionStrategy 'products', 'template'
}
lintOptions {
@@ -146,6 +155,18 @@ android {
}
}
+ dev {
+ initWith debug
+ // Signing and zip-aligning are skipped for prebuilt builds, but
+ // performed for custom builds.
+ zipAlignEnabled shouldZipAlign()
+ if (shouldSign()) {
+ signingConfig signingConfigs.debug
+ } else {
+ signingConfig null
+ }
+ }
+
release {
// Signing and zip-aligning are skipped for prebuilt builds, but
// performed for custom builds.
@@ -167,6 +188,7 @@ android {
assets.srcDirs = ['assets']
}
debug.jniLibs.srcDirs = ['libs/debug', 'libs/debug/vulkan_validation_layers']
+ dev.jniLibs.srcDirs = ['libs/dev']
release.jniLibs.srcDirs = ['libs/release']
}
@@ -183,6 +205,12 @@ task copyAndRenameDebugApk(type: Copy) {
rename "android_debug.apk", getExportFilename()
}
+task copyAndRenameDevApk(type: Copy) {
+ from "$buildDir/outputs/apk/dev/android_dev.apk"
+ into getExportPath()
+ rename "android_dev.apk", getExportFilename()
+}
+
task copyAndRenameReleaseApk(type: Copy) {
from "$buildDir/outputs/apk/release/android_release.apk"
into getExportPath()
@@ -195,6 +223,12 @@ task copyAndRenameDebugAab(type: Copy) {
rename "build-debug.aab", getExportFilename()
}
+task copyAndRenameDevAab(type: Copy) {
+ from "$buildDir/outputs/bundle/dev/build-dev.aab"
+ into getExportPath()
+ rename "build-dev.aab", getExportFilename()
+}
+
task copyAndRenameReleaseAab(type: Copy) {
from "$buildDir/outputs/bundle/release/build-release.aab"
into getExportPath()
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index c238d1b361..1b2976e715 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -76,7 +76,7 @@ ext.getGodotEditorVersion = { ->
String editorVersion = project.hasProperty("godot_editor_version") ? project.property("godot_editor_version") : ""
if (editorVersion == null || editorVersion.isEmpty()) {
// Try the library version first
- editorVersion = getGodotLibraryVersion()
+ editorVersion = getGodotLibraryVersionName()
if (editorVersion.isEmpty()) {
// Fallback value.
@@ -86,9 +86,24 @@ ext.getGodotEditorVersion = { ->
return editorVersion
}
+ext.getGodotLibraryVersionCode = { ->
+ String versionName = ""
+ int versionCode = 1
+ (versionName, versionCode) = getGodotLibraryVersion()
+ return versionCode
+}
+
+ext.getGodotLibraryVersionName = { ->
+ String versionName = ""
+ int versionCode = 1
+ (versionName, versionCode) = getGodotLibraryVersion()
+ return versionName
+}
+
ext.generateGodotLibraryVersion = { List<String> requiredKeys ->
// Attempt to read the version from the `version.py` file.
- String libraryVersion = ""
+ String libraryVersionName = ""
+ int libraryVersionCode = 0
File versionFile = new File("../../../version.py")
if (versionFile.isFile()) {
@@ -109,15 +124,35 @@ ext.generateGodotLibraryVersion = { List<String> requiredKeys ->
}
if (requiredKeys.empty) {
- libraryVersion = map.values().join(".")
+ libraryVersionName = map.values().join(".")
+ try {
+ if (map.containsKey("patch")) {
+ libraryVersionCode = Integer.parseInt(map["patch"])
+ }
+
+ if (map.containsKey("minor")) {
+ libraryVersionCode += (Integer.parseInt(map["minor"]) * 100)
+ }
+
+ if (map.containsKey("major")) {
+ libraryVersionCode += (Integer.parseInt(map["major"]) * 10000)
+ }
+ } catch (NumberFormatException ignore) {
+ libraryVersionCode = 1
+ }
}
}
- if (libraryVersion.isEmpty()) {
+ if (libraryVersionName.isEmpty()) {
// Fallback value in case we're unable to read the file.
- libraryVersion = "custom_build"
+ libraryVersionName = "custom_build"
+ }
+
+ if (libraryVersionCode == 0) {
+ libraryVersionCode = 1
}
- return libraryVersion
+
+ return [libraryVersionName, libraryVersionCode]
}
ext.getGodotLibraryVersion = { ->
@@ -127,7 +162,10 @@ ext.getGodotLibraryVersion = { ->
ext.getGodotPublishVersion = { ->
List<String> requiredKeys = ["major", "minor", "patch", "status"]
- return generateGodotLibraryVersion(requiredKeys)
+ String versionName = ""
+ int versionCode = 1
+ (versionName, versionCode) = generateGodotLibraryVersion(requiredKeys)
+ return versionName
}
final String VALUE_SEPARATOR_REGEX = "\\|"
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index 83bc68c992..e16ca65df5 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -26,21 +26,22 @@ allprojects {
ext {
supportedAbis = ["armv7", "arm64v8", "x86", "x86_64"]
- supportedTargets = ["release", "debug"]
+ supportedTargetsMap = [release: "release", dev: "debug", debug: "release_debug"]
+ supportedFlavors = ["editor", "template"]
- // Used by gradle to specify which architecture to build for by default when running `./gradlew build`.
- // This command is usually used by Android Studio.
+ // Used by gradle to specify which architecture to build for by default when running
+ // `./gradlew build` (this command is usually used by Android Studio).
// If building manually on the command line, it's recommended to use the
- // `./gradlew generateGodotTemplates` build command instead after running the `scons` command.
- // The defaultAbi must be one of the {supportedAbis} values.
- defaultAbi = "arm64v8"
+ // `./gradlew generateGodotTemplates` build command instead after running the `scons` command(s).
+ // The {selectedAbis} values must be from the {supportedAbis} values.
+ selectedAbis = ["arm64v8"]
}
def rootDir = "../../.."
def binDir = "$rootDir/bin/"
-def getSconsTaskName(String buildType) {
- return "compileGodotNativeLibs" + buildType.capitalize()
+def getSconsTaskName(String flavor, String buildType, String abi) {
+ return "compileGodotNativeLibs" + flavor.capitalize() + buildType.capitalize() + abi.capitalize()
}
/**
@@ -55,6 +56,17 @@ task copyDebugBinaryToBin(type: Copy) {
}
/**
+ * Copy the generated 'android_dev.apk' binary template into the Godot bin directory.
+ * Depends on the app build task to ensure the binary is generated prior to copying.
+ */
+task copyDevBinaryToBin(type: Copy) {
+ dependsOn ':app:assembleDev'
+ from('app/build/outputs/apk/dev')
+ into(binDir)
+ include('android_dev.apk')
+}
+
+/**
* Copy the generated 'android_release.apk' binary template into the Godot bin directory.
* Depends on the app build task to ensure the binary is generated prior to copying.
*/
@@ -70,7 +82,7 @@ task copyReleaseBinaryToBin(type: Copy) {
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
task copyDebugAARToAppModule(type: Copy) {
- dependsOn ':lib:assembleDebug'
+ dependsOn ':lib:assembleTemplateDebug'
from('lib/build/outputs/aar')
into('app/libs/debug')
include('godot-lib.debug.aar')
@@ -81,18 +93,40 @@ task copyDebugAARToAppModule(type: Copy) {
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
task copyDebugAARToBin(type: Copy) {
- dependsOn ':lib:assembleDebug'
+ dependsOn ':lib:assembleTemplateDebug'
from('lib/build/outputs/aar')
into(binDir)
include('godot-lib.debug.aar')
}
/**
+ * Copy the Godot android library archive dev file into the app module dev libs directory.
+ * Depends on the library build task to ensure the AAR file is generated prior to copying.
+ */
+task copyDevAARToAppModule(type: Copy) {
+ dependsOn ':lib:assembleTemplateDev'
+ from('lib/build/outputs/aar')
+ into('app/libs/dev')
+ include('godot-lib.dev.aar')
+}
+
+/**
+ * Copy the Godot android library archive dev file into the root bin directory.
+ * Depends on the library build task to ensure the AAR file is generated prior to copying.
+ */
+task copyDevAARToBin(type: Copy) {
+ dependsOn ':lib:assembleTemplateDev'
+ from('lib/build/outputs/aar')
+ into(binDir)
+ include('godot-lib.dev.aar')
+}
+
+/**
* Copy the Godot android library archive release file into the app module release libs directory.
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
task copyReleaseAARToAppModule(type: Copy) {
- dependsOn ':lib:assembleRelease'
+ dependsOn ':lib:assembleTemplateRelease'
from('lib/build/outputs/aar')
into('app/libs/release')
include('godot-lib.release.aar')
@@ -103,7 +137,7 @@ task copyReleaseAARToAppModule(type: Copy) {
* Depends on the library build task to ensure the AAR file is generated prior to copying.
*/
task copyReleaseAARToBin(type: Copy) {
- dependsOn ':lib:assembleRelease'
+ dependsOn ':lib:assembleTemplateRelease'
from('lib/build/outputs/aar')
into(binDir)
include('godot-lib.release.aar')
@@ -111,7 +145,7 @@ task copyReleaseAARToBin(type: Copy) {
/**
* Generate Godot custom build template by zipping the source files from the app directory, as well
- * as the AAR files generated by 'copyDebugAAR' and 'copyReleaseAAR'.
+ * as the AAR files generated by 'copyDebugAAR', 'copyDevAAR' and 'copyReleaseAAR'.
* The zip file also includes some gradle tools to allow building of the custom build.
*/
task zipCustomBuild(type: Zip) {
@@ -130,8 +164,18 @@ def templateExcludedBuildTask() {
def excludedTasks = []
if (!isAndroidStudio()) {
logger.lifecycle("Excluding Android studio build tasks")
- for (String buildType : supportedTargets) {
- excludedTasks += ":lib:" + getSconsTaskName(buildType)
+ for (String flavor : supportedFlavors) {
+ for (String buildType : supportedTargetsMap.keySet()) {
+ if (buildType == "release" && flavor == "editor") {
+ // The editor can't be used with target=release as debugging tools are then not
+ // included, and it would crash on errors instead of reporting them.
+ continue
+ }
+
+ for (String abi : selectedAbis) {
+ excludedTasks += ":lib:" + getSconsTaskName(flavor, buildType, abi)
+ }
+ }
}
}
return excludedTasks
@@ -141,7 +185,7 @@ def templateBuildTasks() {
def tasks = []
// Only build the apks and aar files for which we have native shared libraries.
- for (String target : supportedTargets) {
+ for (String target : supportedTargetsMap.keySet()) {
File targetLibs = new File("lib/libs/" + target)
if (targetLibs != null
&& targetLibs.isDirectory()
@@ -167,6 +211,50 @@ def isAndroidStudio() {
return sysProps != null && sysProps['idea.platform.prefix'] != null
}
+task copyEditorDebugBinaryToBin(type: Copy) {
+ dependsOn ':editor:assembleDebug'
+ from('editor/build/outputs/apk/debug')
+ into(binDir)
+ include('android_editor.apk')
+}
+
+task copyEditorDevBinaryToBin(type: Copy) {
+ dependsOn ':editor:assembleDev'
+ from('editor/build/outputs/apk/dev')
+ into(binDir)
+ include('android_editor_dev.apk')
+}
+
+/**
+ * Generate the Godot Editor Android apk.
+ *
+ * Note: The Godot 'tools' shared libraries must have been generated (via scons) prior to running
+ * this gradle task. The task will only build the apk(s) for which the shared libraries is
+ * available.
+ */
+task generateGodotEditor {
+ gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
+
+ def tasks = []
+
+ for (String target : supportedTargetsMap.keySet()) {
+ if (target == "release") {
+ // The editor can't be used with target=release as debugging tools are then not
+ // included, and it would crash on errors instead of reporting them.
+ continue
+ }
+ File targetLibs = new File("lib/libs/tools/" + target)
+ if (targetLibs != null
+ && targetLibs.isDirectory()
+ && targetLibs.listFiles() != null
+ && targetLibs.listFiles().length > 0) {
+ tasks += "copyEditor${target.capitalize()}BinaryToBin"
+ }
+ }
+
+ dependsOn = tasks
+}
+
/**
* Master task used to coordinate the tasks defined above to generate the set of Godot templates.
*/
@@ -191,7 +279,27 @@ task generateDevTemplate {
}
/**
- * Clean the generated artifacts.
+ * Clean the generated editor artifacts.
+ */
+task cleanGodotEditor(type: Delete) {
+ // Delete the generated native tools libs
+ delete("lib/libs/tools")
+
+ // Delete the library generated AAR files
+ delete("lib/build/outputs/aar")
+
+ // Delete the generated binary apks
+ delete("editor/build/outputs/apk")
+
+ // Delete the Godot editor apks in the Godot bin directory
+ delete("$binDir/android_editor.apk")
+ delete("$binDir/android_editor_dev.apk")
+
+ finalizedBy getTasksByName("clean", true)
+}
+
+/**
+ * Clean the generated template artifacts.
*/
task cleanGodotTemplates(type: Delete) {
// Delete the generated native libs
@@ -208,9 +316,11 @@ task cleanGodotTemplates(type: Delete) {
// Delete the Godot templates in the Godot bin directory
delete("$binDir/android_debug.apk")
+ delete("$binDir/android_dev.apk")
delete("$binDir/android_release.apk")
delete("$binDir/android_source.zip")
delete("$binDir/godot-lib.debug.aar")
+ delete("$binDir/godot-lib.dev.aar")
delete("$binDir/godot-lib.release.aar")
finalizedBy getTasksByName("clean", true)
diff --git a/platform/android/java/editor/build.gradle b/platform/android/java/editor/build.gradle
new file mode 100644
index 0000000000..3312f61ad3
--- /dev/null
+++ b/platform/android/java/editor/build.gradle
@@ -0,0 +1,74 @@
+// Gradle build config for Godot Engine's Android port.
+apply plugin: 'com.android.application'
+
+dependencies {
+ implementation libraries.kotlinStdLib
+ implementation libraries.androidxFragment
+ implementation project(":lib")
+}
+
+android {
+ compileSdkVersion versions.compileSdk
+ buildToolsVersion versions.buildTools
+ ndkVersion versions.ndkVersion
+
+ defaultConfig {
+ // The 'applicationId' suffix allows to install Godot 3.x(v3) and 4.x(v4) on the same device
+ applicationId "org.godotengine.editor.v4"
+ versionCode getGodotLibraryVersionCode()
+ versionName getGodotLibraryVersionName()
+ minSdkVersion versions.minSdk
+ //noinspection ExpiredTargetSdkVersion - Restrict to version 29 until https://github.com/godotengine/godot/pull/51815 is submitted
+ targetSdkVersion 29 // versions.targetSdk
+
+ missingDimensionStrategy 'products', 'editor'
+ }
+
+ compileOptions {
+ sourceCompatibility versions.javaVersion
+ targetCompatibility versions.javaVersion
+ }
+
+ buildTypes {
+ dev {
+ initWith debug
+ applicationIdSuffix ".dev"
+ }
+
+ debug {
+ initWith release
+
+ // Need to swap with the release signing config when this is ready for public release.
+ signingConfig signingConfigs.debug
+ }
+
+ release {
+ // This buildtype is disabled below.
+ // The editor can't be used with target=release only, as debugging tools are then not
+ // included, and it would crash on errors instead of reporting them.
+ }
+ }
+
+ packagingOptions {
+ // 'doNotStrip' is enabled for development within Android Studio
+ if (shouldNotStrip()) {
+ doNotStrip '**/*.so'
+ }
+ }
+
+ // Disable 'release' buildtype.
+ // The editor can't be used with target=release only, as debugging tools are then not
+ // included, and it would crash on errors instead of reporting them.
+ variantFilter { variant ->
+ if (variant.buildType.name == "release") {
+ setIgnore(true)
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all { output ->
+ def suffix = variant.name == "dev" ? "_dev" : ""
+ output.outputFileName = "android_editor${suffix}.apk"
+ }
+ }
+}
diff --git a/platform/android/java/editor/src/dev/res/values/strings.xml b/platform/android/java/editor/src/dev/res/values/strings.xml
new file mode 100644
index 0000000000..45fae3fd39
--- /dev/null
+++ b/platform/android/java/editor/src/dev/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="godot_editor_name_string">Godot Editor 4.x (dev)</string>
+</resources>
diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..0708ffa32f
--- /dev/null
+++ b/platform/android/java/editor/src/main/AndroidManifest.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="org.godotengine.editor"
+ android:installLocation="auto">
+
+ <supports-screens
+ android:largeScreens="true"
+ android:normalScreens="true"
+ android:smallScreens="true"
+ android:xlargeScreens="true" />
+
+ <uses-feature
+ android:glEsVersion="0x00020000"
+ android:required="true" />
+
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <application
+ android:allowBackup="false"
+ android:icon="@mipmap/icon"
+ android:label="@string/godot_editor_name_string"
+ tools:ignore="GoogleAppIndexingWarning"
+ android:requestLegacyExternalStorage="true">
+
+ <activity
+ android:name=".GodotProjectManager"
+ android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
+ android:launchMode="singleTask"
+ android:resizeableActivity="false"
+ android:screenOrientation="landscape"
+ android:exported="true"
+ android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
+ android:process=":GodotProjectManager">
+
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name=".GodotEditor"
+ android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
+ android:process=":GodotEditor"
+ android:launchMode="singleTask"
+ android:resizeableActivity="false"
+ android:screenOrientation="landscape"
+ android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
+ </activity>
+
+ <activity
+ android:name=".GodotGame"
+ android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
+ android:label="@string/godot_project_name_string"
+ android:process=":GodotGame"
+ android:launchMode="singleTask"
+ android:resizeableActivity="false"
+ android:screenOrientation="landscape"
+ android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
+ </activity>
+
+ </application>
+
+</manifest>
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.java b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.java
new file mode 100644
index 0000000000..b3a340cc64
--- /dev/null
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.java
@@ -0,0 +1,110 @@
+/*************************************************************************/
+/* GodotEditor.java */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 org.godotengine.editor;
+
+import org.godotengine.godot.FullScreenGodotApp;
+import org.godotengine.godot.utils.PermissionsUtil;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Debug;
+
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Base class for the Godot Android Editor activities.
+ *
+ * This provides the basic templates for the activities making up this application.
+ * Each derived activity runs in its own process, which enable up to have several instances of
+ * the Godot engine up and running at the same time.
+ *
+ * It also plays the role of the primary editor window.
+ */
+public class GodotEditor extends FullScreenGodotApp {
+ private static final boolean WAIT_FOR_DEBUGGER = false;
+ private static final String COMMAND_LINE_PARAMS = "command_line_params";
+
+ private static final String EDITOR_ARG = "--editor";
+ private static final String PROJECT_MANAGER_ARG = "--project-manager";
+
+ private final List<String> commandLineParams = new ArrayList<>();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ PermissionsUtil.requestManifestPermissions(this);
+
+ String[] params = getIntent().getStringArrayExtra(COMMAND_LINE_PARAMS);
+ updateCommandLineParams(params);
+
+ if (BuildConfig.BUILD_TYPE.equals("debug") && WAIT_FOR_DEBUGGER) {
+ Debug.waitForDebugger();
+ }
+ super.onCreate(savedInstanceState);
+ }
+
+ private void updateCommandLineParams(@Nullable String[] args) {
+ // Update the list of command line params with the new args
+ commandLineParams.clear();
+ if (args != null && args.length > 0) {
+ commandLineParams.addAll(Arrays.asList(args));
+ }
+ }
+
+ @Override
+ public List<String> getCommandLine() {
+ return commandLineParams;
+ }
+
+ @Override
+ public void onNewGodotInstanceRequested(String[] args) {
+ // Parse the arguments to figure out which activity to start.
+ Class<?> targetClass = GodotGame.class;
+ for (String arg : args) {
+ if (EDITOR_ARG.equals(arg)) {
+ targetClass = GodotEditor.class;
+ break;
+ }
+
+ if (PROJECT_MANAGER_ARG.equals(arg)) {
+ targetClass = GodotProjectManager.class;
+ break;
+ }
+ }
+
+ // Launch a new activity
+ Intent newInstance = new Intent(this, targetClass).putExtra(COMMAND_LINE_PARAMS, args);
+ startActivity(newInstance);
+ }
+}
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.java b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.java
new file mode 100644
index 0000000000..5a0be391cf
--- /dev/null
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.java
@@ -0,0 +1,37 @@
+/*************************************************************************/
+/* GodotGame.java */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 org.godotengine.editor;
+
+/**
+ * Drives the 'run project' window of the Godot Editor.
+ */
+public class GodotGame extends GodotEditor {
+}
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.java b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.java
new file mode 100644
index 0000000000..d30f66bb8c
--- /dev/null
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.java
@@ -0,0 +1,41 @@
+/*************************************************************************/
+/* GodotProjectManager.java */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 org.godotengine.editor;
+
+/**
+ * Launcher activity for the Godot Android Editor.
+ *
+ * It presents the user with the project manager interface.
+ * Upon selection of a project, this activity (via its parent logic) starts the
+ * {@link GodotEditor} activity.
+ */
+public class GodotProjectManager extends GodotEditor {
+}
diff --git a/platform/android/java/editor/src/main/res/values/strings.xml b/platform/android/java/editor/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..e8ce34f34d
--- /dev/null
+++ b/platform/android/java/editor/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="godot_editor_name_string">Godot Editor 4.x</string>
+</resources>
diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle
index 120a40a31d..c806de1ded 100644
--- a/platform/android/java/lib/build.gradle
+++ b/platform/android/java/lib/build.gradle
@@ -18,14 +18,13 @@ def pathToRootDir = "../../../../"
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
-
ndkVersion versions.ndkVersion
defaultConfig {
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
- manifestPlaceholders = [godotLibraryVersion: getGodotLibraryVersion()]
+ manifestPlaceholders = [godotLibraryVersion: getGodotLibraryVersionName()]
}
namespace = "org.godotengine.godot"
@@ -35,6 +34,18 @@ android {
targetCompatibility versions.javaVersion
}
+ buildTypes {
+ dev {
+ initWith debug
+ }
+ }
+
+ flavorDimensions "products"
+ productFlavors {
+ editor {}
+ template {}
+ }
+
lintOptions {
abortOnError false
disable 'MissingTranslation', 'UnusedResources'
@@ -58,24 +69,50 @@ android {
aidl.srcDirs = ['aidl']
assets.srcDirs = ['assets']
}
+
debug.jniLibs.srcDirs = ['libs/debug']
+ dev.jniLibs.srcDirs = ['libs/dev']
release.jniLibs.srcDirs = ['libs/release']
+
+ // Editor jni library
+ editorDebug.jniLibs.srcDirs = ['libs/tools/debug']
+ editorDev.jniLibs.srcDirs = ['libs/tools/dev']
+ }
+
+ // Disable 'editorRelease'.
+ // The editor can't be used with target=release as debugging tools are then not
+ // included, and it would crash on errors instead of reporting them.
+ variantFilter { variant ->
+ if (variant.name == "editorRelease") {
+ setIgnore(true)
+ }
}
libraryVariants.all { variant ->
- variant.outputs.all { output ->
- output.outputFileName = "godot-lib.${variant.name}.aar"
+ def flavorName = variant.getFlavorName()
+ if (flavorName == null || flavorName == "") {
+ throw new GradleException("Invalid product flavor: $flavorName")
}
- def buildType = variant.buildType.name.capitalize()
+ boolean toolsFlag = flavorName == "editor"
- def releaseTarget = buildType.toLowerCase()
- if (releaseTarget == null || releaseTarget == "") {
- throw new GradleException("Invalid build type: " + buildType)
+ def buildType = variant.buildType.name
+ if (buildType == null || buildType == "" || !supportedTargetsMap.containsKey(buildType)) {
+ throw new GradleException("Invalid build type: $buildType")
}
- if (!supportedAbis.contains(defaultAbi)) {
- throw new GradleException("Invalid default abi: " + defaultAbi)
+ def sconsTarget = supportedTargetsMap[buildType]
+ if (sconsTarget == null || sconsTarget == "") {
+ throw new GradleException("Invalid scons target: $sconsTarget")
+ }
+
+ // Update the name of the generated library
+ def outputSuffix = "${buildType}.aar"
+ if (toolsFlag) {
+ outputSuffix = "tools.$outputSuffix"
+ }
+ variant.outputs.all { output ->
+ output.outputFileName = "godot-lib.${outputSuffix}"
}
// Find scons' executable path
@@ -88,13 +125,11 @@ android {
for (ext in sconsExts) {
String sconsNameExt = sconsName + ext
logger.lifecycle("Checking $sconsNameExt")
-
sconsExecutableFile = org.gradle.internal.os.OperatingSystem.current().findInPath(sconsNameExt)
if (sconsExecutableFile != null) {
// We're done!
break
}
-
// Check all the options in path
List<File> allOptions = org.gradle.internal.os.OperatingSystem.current().findAllInPath(sconsNameExt)
if (!allOptions.isEmpty()) {
@@ -103,27 +138,32 @@ android {
break
}
}
-
if (sconsExecutableFile == null) {
throw new GradleException("Unable to find executable path for the '$sconsName' command.")
} else {
logger.lifecycle("Found executable path for $sconsName: ${sconsExecutableFile.absolutePath}")
}
- // Creating gradle task to generate the native libraries for the default abi.
- def taskName = getSconsTaskName(buildType)
- tasks.create(name: taskName, type: Exec) {
- executable sconsExecutableFile.absolutePath
- args "--directory=${pathToRootDir}", "platform=android", "target=${releaseTarget}", "android_arch=${defaultAbi}", "-j" + Runtime.runtime.availableProcessors()
- }
+ for (String selectedAbi : selectedAbis) {
+ if (!supportedAbis.contains(selectedAbi)) {
+ throw new GradleException("Invalid selected abi: $selectedAbi")
+ }
- // Schedule the tasks so the generated libs are present before the aar file is packaged.
- tasks["merge${buildType}JniLibFolders"].dependsOn taskName
+ // Creating gradle task to generate the native libraries for the selected abi.
+ def taskName = getSconsTaskName(flavorName, buildType, selectedAbi)
+ tasks.create(name: taskName, type: Exec) {
+ executable sconsExecutableFile.absolutePath
+ args "--directory=${pathToRootDir}", "platform=android", "tools=${toolsFlag}", "target=${sconsTarget}", "android_arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors()
+ }
+
+ // Schedule the tasks so the generated libs are present before the aar file is packaged.
+ tasks["merge${flavorName.capitalize()}${buildType.capitalize()}JniLibFolders"].dependsOn taskName
+ }
}
// TODO: Enable when issues with AGP 7.1+ are resolved (https://github.com/GodotVR/godot_openxr/issues/187).
// publishing {
-// singleVariant("release") {
+// singleVariant("templateRelease") {
// withSourcesJar()
// withJavadocJar()
// }
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
index 78848c109a..8a86136daf 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -47,7 +47,6 @@ import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.ClipData;
import android.content.ClipboardManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -333,9 +332,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
public void restart() {
- if (godotHost != null) {
- godotHost.onGodotRestartRequested(this);
- }
+ runOnUiThread(() -> {
+ if (godotHost != null) {
+ godotHost.onGodotRestartRequested(this);
+ }
+ });
}
public void alert(final String message, final String title) {
@@ -859,9 +860,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
private void forceQuit() {
// TODO: This is a temp solution. The proper fix will involve tracking down and properly shutting down each
// native Godot components that is started in Godot#onVideoInit.
- if (godotHost != null) {
- godotHost.onGodotForceQuit(this);
- }
+ runOnUiThread(() -> {
+ if (godotHost != null) {
+ godotHost.onGodotForceQuit(this);
+ }
+ });
}
private boolean obbIsCorrupted(String f, String main_pack_md5) {
@@ -1010,6 +1013,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
mProgressFraction.setText(Helpers.getDownloadProgressString(progress.mOverallProgress,
progress.mOverallTotal));
}
+
public void initInputDevices() {
mRenderView.initInputDevices();
}
@@ -1018,4 +1022,13 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
private GodotRenderView getRenderView() { // used by native side to get renderView
return mRenderView;
}
+
+ @Keep
+ private void createNewGodotInstance(String[] args) {
+ runOnUiThread(() -> {
+ if (godotHost != null) {
+ godotHost.onNewGodotInstanceRequested(args);
+ }
+ });
+ }
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
index 8e8f993369..2e7b67194f 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java
@@ -60,8 +60,16 @@ public interface GodotHost {
default void onGodotForceQuit(Godot instance) {}
/**
- * Invoked on the GL thread when the Godot instance wants to be restarted. It's up to the host
+ * Invoked on the UI thread when the Godot instance wants to be restarted. It's up to the host
* to perform the appropriate action(s).
*/
default void onGodotRestartRequested(Godot instance) {}
+
+ /**
+ * Invoked on the UI thread when a new Godot instance is requested. It's up to the host to
+ * perform the appropriate action(s).
+ *
+ * @param args Arguments used to initialize the new instance.
+ */
+ default void onNewGodotInstanceRequested(String[] args) {}
}
diff --git a/platform/android/java/nativeSrcsConfigs/CMakeLists.txt b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
index 966c02f7d7..59cfe21c49 100644
--- a/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
+++ b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
@@ -16,3 +16,5 @@ add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS})
target_include_directories(${PROJECT_NAME}
SYSTEM PUBLIC
${GODOT_ROOT_DIR})
+
+add_definitions(-DUNIX_ENABLED -DVULKAN_ENABLED -DTOOLS_ENABLED)
diff --git a/platform/android/java/nativeSrcsConfigs/build.gradle b/platform/android/java/nativeSrcsConfigs/build.gradle
index 158bb2b98e..0cb769b539 100644
--- a/platform/android/java/nativeSrcsConfigs/build.gradle
+++ b/platform/android/java/nativeSrcsConfigs/build.gradle
@@ -6,6 +6,7 @@ plugins {
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
+ ndkVersion versions.ndkVersion
defaultConfig {
minSdkVersion versions.minSdk
@@ -28,8 +29,6 @@ android {
}
}
- ndkVersion versions.ndkVersion
-
externalNativeBuild {
cmake {
path "CMakeLists.txt"
diff --git a/platform/android/java/scripts/publish-module.gradle b/platform/android/java/scripts/publish-module.gradle
index 6b2aea5caf..32b749e493 100644
--- a/platform/android/java/scripts/publish-module.gradle
+++ b/platform/android/java/scripts/publish-module.gradle
@@ -7,20 +7,15 @@ version = PUBLISH_VERSION
afterEvaluate {
publishing {
publications {
- release(MavenPublication) {
+ templateRelease(MavenPublication) {
+ from components.templateRelease
+
// The coordinates of the library, being set from variables that
// we'll set up later
groupId ossrhGroupId
artifactId PUBLISH_ARTIFACT_ID
version PUBLISH_VERSION
- // Two artifacts, the `aar` (or `jar`) and the sources
- if (project.plugins.findPlugin("com.android.library")) {
- from components.release
- } else {
- from components.java
- }
-
// Mostly self-explanatory metadata
pom {
name = PUBLISH_ARTIFACT_ID
diff --git a/platform/android/java/settings.gradle b/platform/android/java/settings.gradle
index 584b626900..56e1b6fd3a 100644
--- a/platform/android/java/settings.gradle
+++ b/platform/android/java/settings.gradle
@@ -4,6 +4,7 @@ rootProject.name = "Godot"
include ':app'
include ':lib'
include ':nativeSrcsConfigs'
+include ':editor'
include ':assetPacks:installTime'
project(':assetPacks:installTime').projectDir = file("app/assetPacks/installTime")
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 249717921f..658f9281ab 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -107,6 +107,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz) {
// lets cleanup
+ if (java_class_wrapper) {
+ memdelete(java_class_wrapper);
+ }
if (godot_io_java) {
delete godot_io_java;
}
@@ -117,6 +120,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env
delete input_handler;
}
if (os_android) {
+ os_android->main_loop_end();
delete os_android;
}
}
@@ -146,7 +150,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jc
}
}
- Error err = Main::setup("apk", cmdlen, (char **)cmdline, false);
+ Error err = Main::setup(OS_Android::ANDROID_EXEC_PATH, cmdlen, (char **)cmdline, false);
if (cmdline) {
if (j_cmdline) {
for (int i = 0; i < cmdlen; ++i) {
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index 754267c834..2c8378e685 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -77,6 +77,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
_get_input_fallback_mapping = p_env->GetMethodID(godot_class, "getInputFallbackMapping", "()Ljava/lang/String;");
_on_godot_setup_completed = p_env->GetMethodID(godot_class, "onGodotSetupCompleted", "()V");
_on_godot_main_loop_started = p_env->GetMethodID(godot_class, "onGodotMainLoopStarted", "()V");
+ _create_new_godot_instance = p_env->GetMethodID(godot_class, "createNewGodotInstance", "([Ljava/lang/String;)V");
// get some Activity method pointers...
_get_class_loader = p_env->GetMethodID(activity_class, "getClassLoader", "()Ljava/lang/ClassLoader;");
@@ -351,3 +352,16 @@ void GodotJavaWrapper::vibrate(int p_duration_ms) {
env->CallVoidMethod(godot_instance, _vibrate, p_duration_ms);
}
}
+
+void GodotJavaWrapper::create_new_godot_instance(List<String> args) {
+ if (_create_new_godot_instance) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
+
+ jobjectArray jargs = env->NewObjectArray(args.size(), env->FindClass("java/lang/String"), env->NewStringUTF(""));
+ for (int i = 0; i < args.size(); i++) {
+ env->SetObjectArrayElement(jargs, i, env->NewStringUTF(args[i].utf8().get_data()));
+ }
+ env->CallVoidMethod(godot_instance, _create_new_godot_instance, jargs);
+ }
+}
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index 42ae91480f..f04fda7c3d 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -37,6 +37,7 @@
#include <android/log.h>
#include <jni.h>
+#include "core/templates/list.h"
#include "java_godot_view_wrapper.h"
#include "string_android.h"
@@ -70,6 +71,7 @@ private:
jmethodID _on_godot_setup_completed = 0;
jmethodID _on_godot_main_loop_started = 0;
jmethodID _get_class_loader = 0;
+ jmethodID _create_new_godot_instance = 0;
public:
GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_godot_instance);
@@ -103,6 +105,7 @@ public:
bool is_activity_resumed();
void vibrate(int p_duration_ms);
String get_input_fallback_mapping();
+ void create_new_godot_instance(List<String> args);
};
#endif /* !JAVA_GODOT_WRAPPER_H */
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index b17b0f3139..438fc04eb6 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -35,6 +35,7 @@
#include "drivers/unix/file_access_unix.h"
#include "main/main.h"
#include "platform/android/display_server_android.h"
+#include "scene/main/scene_tree.h"
#include "dir_access_jandroid.h"
#include "file_access_android.h"
@@ -45,6 +46,8 @@
#include "java_godot_io_wrapper.h"
#include "java_godot_wrapper.h"
+const char *OS_Android::ANDROID_EXEC_PATH = "apk";
+
String _remove_symlink(const String &dir) {
// Workaround for Android 6.0+ using a symlink.
// Save the current directory.
@@ -81,18 +84,28 @@ void OS_Android::alert(const String &p_alert, const String &p_title) {
void OS_Android::initialize_core() {
OS_Unix::initialize_core();
+#ifdef TOOLS_ENABLED
+ FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
+#else
if (use_apk_expansion) {
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
} else {
FileAccess::make_default<FileAccessAndroid>(FileAccess::ACCESS_RESOURCES);
}
+#endif
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA);
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_FILESYSTEM);
+
+#ifdef TOOLS_ENABLED
+ DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_RESOURCES);
+#else
if (use_apk_expansion) {
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_RESOURCES);
} else {
DirAccess::make_default<DirAccessJAndroid>(DirAccess::ACCESS_RESOURCES);
}
+#endif
+
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA);
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM);
@@ -178,6 +191,10 @@ bool OS_Android::main_loop_iterate() {
void OS_Android::main_loop_end() {
if (main_loop) {
+ SceneTree *scene_tree = Object::cast_to<SceneTree>(main_loop);
+ if (scene_tree) {
+ scene_tree->quit();
+ }
main_loop->finalize();
}
}
@@ -197,7 +214,11 @@ Error OS_Android::shell_open(String p_uri) {
}
String OS_Android::get_resource_dir() const {
+#ifdef TOOLS_ENABLED
+ return OS_Unix::get_resource_dir();
+#else
return "/"; //android has its own filesystem for resources inside the APK
+#endif
}
String OS_Android::get_locale() const {
@@ -222,6 +243,14 @@ String OS_Android::get_data_path() const {
return get_user_data_dir();
}
+String OS_Android::get_executable_path() const {
+ // Since unix process creation is restricted on Android, we bypass
+ // OS_Unix::get_executable_path() so we can return ANDROID_EXEC_PATH.
+ // Detection of ANDROID_EXEC_PATH allows to handle process creation in an Android compliant
+ // manner.
+ return OS::get_executable_path();
+}
+
String OS_Android::get_user_data_dir() const {
if (!data_dir_cache.is_empty()) {
return data_dir_cache;
@@ -294,6 +323,10 @@ void OS_Android::vibrate_handheld(int p_duration_ms) {
godot_java->vibrate(p_duration_ms);
}
+String OS_Android::get_config_path() const {
+ return get_user_data_dir().plus_file("config");
+}
+
bool OS_Android::_check_internal_feature_support(const String &p_feature) {
if (p_feature == "mobile") {
return true;
@@ -343,5 +376,26 @@ OS_Android::OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_god
DisplayServerAndroid::register_android_driver();
}
+Error OS_Android::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) {
+ if (p_path == ANDROID_EXEC_PATH) {
+ return create_instance(p_arguments);
+ } else {
+ return OS_Unix::execute(p_path, p_arguments, r_pipe, r_exitcode, read_stderr, p_pipe_mutex, p_open_console);
+ }
+}
+
+Error OS_Android::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) {
+ if (p_path == ANDROID_EXEC_PATH) {
+ return create_instance(p_arguments, r_child_id);
+ } else {
+ return OS_Unix::create_process(p_path, p_arguments, r_child_id, p_open_console);
+ }
+}
+
+Error OS_Android::create_instance(const List<String> &p_arguments, ProcessID *r_child_id) {
+ godot_java->create_new_godot_instance(p_arguments);
+ return OK;
+}
+
OS_Android::~OS_Android() {
}
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index f523f172c6..c0c5127224 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -66,6 +66,8 @@ private:
GodotIOJavaWrapper *godot_io_java;
public:
+ static const char *ANDROID_EXEC_PATH;
+
virtual void initialize_core() override;
virtual void initialize() override;
@@ -108,6 +110,7 @@ public:
ANativeWindow *get_native_window() const;
virtual Error shell_open(String p_uri) override;
+ virtual String get_executable_path() const override;
virtual String get_user_data_dir() const override;
virtual String get_data_path() const override;
virtual String get_cache_path() const override;
@@ -121,6 +124,12 @@ public:
void vibrate_handheld(int p_duration_ms) override;
+ virtual String get_config_path() const override;
+
+ virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
+ virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
+ virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
+
virtual bool _check_internal_feature_support(const String &p_feature) override;
OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion);
~OS_Android();