Backported the 3.x android editor port pr by m4gr3d and thebestnom. (Godot pr 57747).

This commit is contained in:
Relintai 2022-03-22 19:35:57 +01:00
parent 66be182f51
commit b74c00ea65
23 changed files with 731 additions and 57 deletions

View File

@ -479,6 +479,10 @@ void EditorNode::_notification(int p_what) {
get_tree()->get_root()->set_as_audio_listener(false);
get_tree()->get_root()->set_as_audio_listener_2d(false);
get_tree()->set_auto_accept_quit(false);
//Note that this class will only compile if tools is enabled
#ifdef ANDROID_ENABLED
get_tree()->set_quit_on_go_back(false);
#endif
get_tree()->connect("files_dropped", this, "_dropped_files");
get_tree()->connect("global_menu_action", this, "_global_menu_action");

View File

@ -50,10 +50,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
lib_tools_dir = ""
if env["tools"]:
lib_tools_dir = "tools/"
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")
)

View File

@ -42,6 +42,11 @@ allprojects {
}
}
configurations {
// Initializes a placeholder for the devImplementation dependency configuration.
devImplementation {}
}
dependencies {
implementation libraries.kotlinStdLib
implementation libraries.androidxFragment
@ -54,6 +59,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'])
}
@ -78,6 +84,7 @@ dependencies {
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
ndkVersion versions.ndkVersion
compileOptions {
sourceCompatibility versions.javaVersion
@ -105,6 +112,8 @@ android {
versionName getExportVersionName()
minSdkVersion getExportMinSdkVersion()
targetSdkVersion getExportTargetSdkVersion()
missingDimensionStrategy 'products', 'template'
//CHUNK_ANDROID_DEFAULTCONFIG_BEGIN
//CHUNK_ANDROID_DEFAULTCONFIG_END
}
@ -160,6 +169,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.
@ -200,6 +221,11 @@ android {
'libs/debug'
//DIR_JNI_DEBUG_BEGIN
//DIR_JNI_DEBUG_END
]
dev.jniLibs.srcDirs = [
'libs/dev'
//DIR_JNI_DEV_BEGIN
//DIR_JNI_DEV_END
]
release.jniLibs.srcDirs = [
'libs/release'
@ -221,6 +247,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()
@ -233,6 +265,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()

View File

@ -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.getGodotLibraryVersion = { ->
// Attempt to read the version from the `version.py` file.
String libraryVersion = ""
String libraryVersionName = ""
int libraryVersionCode = 1
File versionFile = new File("../../../version.py")
if (versionFile.isFile()) {
@ -110,15 +125,20 @@ ext.getGodotLibraryVersion = { ->
}
if (requiredKeys.empty) {
libraryVersion = map.values().join(".")
libraryVersionName = map.values().join(".")
try {
libraryVersionCode = Integer.parseInt(map["patch"]) +
(Integer.parseInt(map["minor"]) * 100) +
(Integer.parseInt(map["major"]) * 10000)
} catch (NumberFormatException ignore) {}
}
}
if (libraryVersion.isEmpty()) {
if (libraryVersionName.isEmpty()) {
// Fallback value in case we're unable to read the file.
libraryVersion = "custom_build"
libraryVersionName = "custom_build"
}
return libraryVersion
return [libraryVersionName, libraryVersionCode]
}
final String VALUE_SEPARATOR_REGEX = "\\|"

View File

@ -22,21 +22,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()
}
/**
@ -50,6 +51,17 @@ task copyDebugBinaryToBin(type: Copy) {
include('android_debug.apk')
}
/**
* 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.
@ -66,7 +78,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')
@ -77,18 +89,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')
@ -99,7 +133,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:assembleToolsDisabledRelease'
from('lib/build/outputs/aar')
into(binDir)
include('godot-lib.release.aar')
@ -107,7 +141,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) {
@ -126,8 +160,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
@ -137,7 +181,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()
@ -163,6 +207,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.
*/
@ -187,7 +275,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
@ -204,9 +312,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)

View File

@ -0,0 +1,73 @@
// 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.v3"
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"
}
}
}

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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 {
}

View File

@ -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 {
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_editor_name_string">Godot Editor</string>
</resources>

View File

@ -11,14 +11,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()]
}
compileOptions {
@ -26,6 +25,18 @@ android {
targetCompatibility versions.javaVersion
}
buildTypes {
dev {
initWith debug
}
}
flavorDimensions "products"
productFlavors {
editor {}
template {}
}
lintOptions {
abortOnError false
disable 'MissingTranslation', 'UnusedResources'
@ -49,24 +60,52 @@ 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 ->
def flavorName = variant.getFlavorName()
if (flavorName == null || flavorName == "") {
throw new GradleException("Invalid product flavor: $flavorName")
}
boolean toolsFlag = flavorName == "editor"
def buildType = variant.buildType.name
if (buildType == null || buildType == "" || !supportedTargetsMap.containsKey(buildType)) {
throw new GradleException("Invalid build type: $buildType")
}
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.${variant.name}.aar"
}
def buildType = variant.buildType.name.capitalize()
def releaseTarget = buildType.toLowerCase()
if (releaseTarget == null || releaseTarget == "") {
throw new GradleException("Invalid build type: " + buildType)
}
if (!supportedAbis.contains(defaultAbi)) {
throw new GradleException("Invalid default abi: " + defaultAbi)
output.outputFileName = "godot-lib.${outputSuffix}"
}
// Find scons' executable path
@ -101,14 +140,20 @@ android {
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
}
}
}

View File

@ -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;
@ -449,9 +448,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) {
@ -995,9 +996,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) {
@ -1146,7 +1149,17 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
mProgressFraction.setText(Helpers.getDownloadProgressString(progress.mOverallProgress,
progress.mOverallTotal));
}
public void initInputDevices() {
mView.initInputDevices();
}
@Keep
private void createNewGodotInstance(String[] args) {
runOnUiThread(() -> {
if (godotHost != null) {
godotHost.onNewGodotInstanceRequested(args);
}
});
}
}

View File

@ -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) {}
}

View File

@ -17,3 +17,5 @@ target_include_directories(${PROJECT_NAME}
SYSTEM PUBLIC
${GODOT_ROOT_DIR}
${GODOT_ROOT_DIR}/modules/gdnative/include)
add_definitions(-DUNIX_ENABLED -DTOOLS_ENABLED)

View File

@ -6,6 +6,7 @@ plugins {
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
ndkVersion versions.ndkVersion
defaultConfig {
minSdkVersion versions.minSdk

View File

@ -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")

View File

@ -148,6 +148,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;
}
@ -188,7 +191,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) {

View File

@ -80,6 +80,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;");
@ -369,3 +370,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);
}
}

View File

@ -36,6 +36,7 @@
#include <android/log.h>
#include <jni.h>
#include "core/list.h"
#include "string_android.h"
// Class that makes functions in java/src/org/godotengine/godot/Godot.java callable from C++
@ -69,6 +70,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);
@ -105,6 +107,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 */

View File

@ -35,6 +35,7 @@
#include "drivers/unix/dir_access_unix.h"
#include "drivers/unix/file_access_unix.h"
#include "main/main.h"
#include "scene/main/scene_tree.h"
#include "servers/visual/visual_server_raster.h"
#include "servers/visual/visual_server_wrap_mt.h"
@ -49,6 +50,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.
@ -97,17 +100,28 @@ const char *OS_Android::get_audio_driver_name(int p_driver) const {
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);
@ -263,6 +277,15 @@ void OS_Android::set_keep_screen_on(bool p_enabled) {
godot_java->set_keep_screen_on(p_enabled);
}
void OS_Android::set_low_processor_usage_mode(bool p_enabled) {
#ifdef TOOLS_ENABLED
// Disabled as it causes flickers. We also expect the devices running Godot in editor mode to be high end.
OS_Unix::set_low_processor_usage_mode(false);
#else
OS_Unix::set_low_processor_usage_mode(p_enabled);
#endif
}
Size2 OS_Android::get_window_size() const {
return Vector2(default_videomode.width, default_videomode.height);
}
@ -297,8 +320,15 @@ bool OS_Android::main_loop_iterate() {
}
void OS_Android::main_loop_end() {
if (main_loop)
if (main_loop) {
SceneTree *scene_tree = Object::cast_to<SceneTree>(main_loop);
if (scene_tree) {
scene_tree->quit();
}
main_loop->finish();
}
}
void OS_Android::main_loop_focusout() {
@ -377,7 +407,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 {
@ -436,6 +470,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 != String())
return data_dir_cache;
@ -508,6 +550,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") {
//TODO support etc2 only if GLES3 driver is selected
@ -551,5 +597,18 @@ OS_Android::OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_god
AudioDriverManager::add_driver(&audio_driver_android);
}
Error OS_Android::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, 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, r_child_id);
} else {
return OS_Unix::execute(p_path, p_arguments, p_blocking, r_child_id, r_pipe, r_exitcode, read_stderr, p_pipe_mutex, 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() {
}

View File

@ -69,6 +69,8 @@ class OS_Android : public OS_Unix {
bool transparency_enabled = false;
public:
static const char *ANDROID_EXEC_PATH;
// functions used by main to initialize/deinitialize the OS
virtual int get_video_driver_count() const;
virtual const char *get_video_driver_name(int p_driver) const;
@ -110,6 +112,7 @@ public:
virtual void get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen = 0) const;
virtual void set_keep_screen_on(bool p_enabled);
virtual void set_low_processor_usage_mode(bool p_enabled);
virtual Size2 get_window_size() const;
virtual Rect2 get_window_safe_area() const;
@ -143,6 +146,7 @@ public:
virtual ScreenOrientation get_screen_orientation() const;
virtual Error shell_open(String p_uri);
virtual String get_executable_path() const;
virtual String get_user_data_dir() const;
virtual String get_data_path() const;
virtual String get_cache_path() const;
@ -172,9 +176,15 @@ public:
virtual String get_joy_guid(int p_device) const;
void vibrate_handheld(int p_duration_ms);
virtual String get_config_path() const;
virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking = true, ProcessID *r_child_id = nullptr, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false);
virtual bool _check_internal_feature_support(const String &p_feature);
OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion);
~OS_Android();
private:
Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id);
};
#endif

View File

@ -1380,6 +1380,10 @@ Vector2 Viewport::_get_window_offset() const {
}
Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) {
if (ev.is_null()) {
return ev;
}
Vector2 vp_ofs = _get_window_offset();
Transform2D ai = get_final_transform().affine_inverse() * _get_input_pre_xform();