summaryrefslogtreecommitdiffstats
path: root/platform/android
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android')
-rw-r--r--platform/android/SCsub5
-rw-r--r--platform/android/android_input_handler.cpp22
-rw-r--r--platform/android/android_input_handler.h4
-rw-r--r--platform/android/android_keys_utils.h1
-rw-r--r--platform/android/detect.py3
-rw-r--r--platform/android/doc_classes/EditorExportPlatformAndroid.xml2
-rw-r--r--platform/android/java/app/config.gradle10
-rw-r--r--platform/android/java/build.gradle58
-rw-r--r--platform/android/java/editor/build.gradle92
-rw-r--r--platform/android/java/editor/src/debug/res/values/strings.xml4
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt3
-rw-r--r--platform/android/java/lib/build.gradle23
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java69
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java10
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java5
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java10
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt19
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java31
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt8
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java20
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt122
-rw-r--r--platform/android/java_godot_lib_jni.cpp7
-rw-r--r--platform/android/java_godot_lib_jni.h4
-rw-r--r--platform/android/java_godot_view_wrapper.cpp13
-rw-r--r--platform/android/java_godot_view_wrapper.h1
-rw-r--r--platform/android/java_godot_wrapper.cpp30
-rw-r--r--platform/android/java_godot_wrapper.h6
-rw-r--r--platform/android/os_android.cpp31
-rw-r--r--platform/android/os_android.h4
-rw-r--r--platform/android/tts_android.cpp42
-rw-r--r--platform/android/tts_android.h3
32 files changed, 518 insertions, 148 deletions
diff --git a/platform/android/SCsub b/platform/android/SCsub
index e4d04f1df9..1f3bbc2350 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -56,7 +56,10 @@ if lib_arch_dir != "":
if env.dev_build:
lib_type_dir = "dev"
elif env.debug_features:
- lib_type_dir = "debug"
+ if env.editor_build and env["store_release"]:
+ lib_type_dir = "release"
+ else:
+ lib_type_dir = "debug"
else: # Release
lib_type_dir = "release"
diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp
index 63045237e9..37a019eaa4 100644
--- a/platform/android/android_input_handler.cpp
+++ b/platform/android/android_input_handler.cpp
@@ -138,22 +138,19 @@ void AndroidInputHandler::process_key_event(int p_physical_keycode, int p_unicod
}
void AndroidInputHandler::_cancel_all_touch() {
- _parse_all_touch(false, false, true);
+ _parse_all_touch(false, true);
touch.clear();
}
-void AndroidInputHandler::_parse_all_touch(bool p_pressed, bool p_double_tap, bool reset_index) {
+void AndroidInputHandler::_parse_all_touch(bool p_pressed, bool p_canceled, bool p_double_tap) {
if (touch.size()) {
//end all if exist
for (int i = 0; i < touch.size(); i++) {
Ref<InputEventScreenTouch> ev;
ev.instantiate();
- if (reset_index) {
- ev->set_index(-1);
- } else {
- ev->set_index(touch[i].id);
- }
+ ev->set_index(touch[i].id);
ev->set_pressed(p_pressed);
+ ev->set_canceled(p_canceled);
ev->set_position(touch[i].pos);
ev->set_double_tap(p_double_tap);
Input::get_singleton()->parse_input_event(ev);
@@ -180,7 +177,7 @@ void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const
}
//send touch
- _parse_all_touch(true, p_double_tap);
+ _parse_all_touch(true, false, p_double_tap);
} break;
case AMOTION_EVENT_ACTION_MOVE: { //motion
@@ -257,11 +254,11 @@ void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const
void AndroidInputHandler::_cancel_mouse_event_info(bool p_source_mouse_relative) {
buttons_state = BitField<MouseButtonMask>();
- _parse_mouse_event_info(BitField<MouseButtonMask>(), false, false, p_source_mouse_relative);
+ _parse_mouse_event_info(BitField<MouseButtonMask>(), false, true, false, p_source_mouse_relative);
mouse_event_info.valid = false;
}
-void AndroidInputHandler::_parse_mouse_event_info(BitField<MouseButtonMask> event_buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative) {
+void AndroidInputHandler::_parse_mouse_event_info(BitField<MouseButtonMask> event_buttons_mask, bool p_pressed, bool p_canceled, bool p_double_click, bool p_source_mouse_relative) {
if (!mouse_event_info.valid) {
return;
}
@@ -278,6 +275,7 @@ void AndroidInputHandler::_parse_mouse_event_info(BitField<MouseButtonMask> even
hover_prev_pos = mouse_event_info.pos;
}
ev->set_pressed(p_pressed);
+ ev->set_canceled(p_canceled);
BitField<MouseButtonMask> changed_button_mask = BitField<MouseButtonMask>(buttons_state.operator int64_t() ^ event_buttons_mask.operator int64_t());
buttons_state = event_buttons_mask;
@@ -289,7 +287,7 @@ void AndroidInputHandler::_parse_mouse_event_info(BitField<MouseButtonMask> even
}
void AndroidInputHandler::_release_mouse_event_info(bool p_source_mouse_relative) {
- _parse_mouse_event_info(BitField<MouseButtonMask>(), false, false, p_source_mouse_relative);
+ _parse_mouse_event_info(BitField<MouseButtonMask>(), false, false, false, p_source_mouse_relative);
mouse_event_info.valid = false;
}
@@ -318,7 +316,7 @@ void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_an
mouse_event_info.valid = true;
mouse_event_info.pos = p_event_pos;
- _parse_mouse_event_info(event_buttons_mask, true, p_double_click, p_source_mouse_relative);
+ _parse_mouse_event_info(event_buttons_mask, true, false, p_double_click, p_source_mouse_relative);
} break;
case AMOTION_EVENT_ACTION_CANCEL: {
diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h
index 2badd32636..42d1c228a8 100644
--- a/platform/android/android_input_handler.h
+++ b/platform/android/android_input_handler.h
@@ -83,13 +83,13 @@ private:
void _wheel_button_click(BitField<MouseButtonMask> event_buttons_mask, const Ref<InputEventMouseButton> &ev, MouseButton wheel_button, float factor);
- void _parse_mouse_event_info(BitField<MouseButtonMask> event_buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative);
+ void _parse_mouse_event_info(BitField<MouseButtonMask> event_buttons_mask, bool p_pressed, bool p_canceled, bool p_double_click, bool p_source_mouse_relative);
void _release_mouse_event_info(bool p_source_mouse_relative = false);
void _cancel_mouse_event_info(bool p_source_mouse_relative = false);
- void _parse_all_touch(bool p_pressed, bool p_double_tap, bool reset_index = false);
+ void _parse_all_touch(bool p_pressed, bool p_canceled = false, bool p_double_tap = false);
void _release_all_touch();
diff --git a/platform/android/android_keys_utils.h b/platform/android/android_keys_utils.h
index 3a587dd680..bdfaaa3215 100644
--- a/platform/android/android_keys_utils.h
+++ b/platform/android/android_keys_utils.h
@@ -60,6 +60,7 @@ static AndroidGodotCodePair android_godot_code_pairs[] = {
{ AKEYCODE_DPAD_DOWN, Key::DOWN }, // (20) Directional Pad Down key.
{ AKEYCODE_DPAD_LEFT, Key::LEFT }, // (21) Directional Pad Left key.
{ AKEYCODE_DPAD_RIGHT, Key::RIGHT }, // (22) Directional Pad Right key.
+ { AKEYCODE_DPAD_CENTER, Key::ENTER }, // (23) Directional Pad Center key.
{ AKEYCODE_VOLUME_UP, Key::VOLUMEUP }, // (24) Volume Up key.
{ AKEYCODE_VOLUME_DOWN, Key::VOLUMEDOWN }, // (25) Volume Down key.
{ AKEYCODE_POWER, Key::STANDBY }, // (26) Power key.
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 7515d0020d..20aced3524 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -22,6 +22,8 @@ def can_build():
def get_opts():
+ from SCons.Variables import BoolVariable
+
return [
("ANDROID_SDK_ROOT", "Path to the Android SDK", get_env_android_sdk_root()),
(
@@ -29,6 +31,7 @@ def get_opts():
'Target platform (android-<api>, e.g. "android-' + str(get_min_target_api()) + '")',
"android-" + str(get_min_target_api()),
),
+ BoolVariable("store_release", "Editor build for Google Play Store (for official builds only)", False),
]
diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
index 11129ca149..0652ba5c0e 100644
--- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml
+++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
@@ -423,7 +423,7 @@
Allows an application to record audio. See [url=https://developer.android.com/reference/android/Manifest.permission#RECORD_AUDIO]RECORD_AUDIO[/url].
</member>
<member name="permissions/reorder_tasks" type="bool" setter="" getter="">
- Allows an application to change the Z-order of tasks. See [url= https://developer.android.com/reference/android/Manifest.permission#REORDER_TASKS]REORDER_TASKS[/url].
+ Allows an application to change the Z-order of tasks. See [url=https://developer.android.com/reference/android/Manifest.permission#REORDER_TASKS]REORDER_TASKS[/url].
</member>
<member name="permissions/restart_packages" type="bool" setter="" getter="">
Deprecated in API level 15.
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index 4bac6c814a..e7c06628c8 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -135,14 +135,16 @@ ext.generateGodotLibraryVersion = { List<String> requiredKeys ->
String statusValue = map["status"]
if (statusValue == null) {
statusCode = 0
- } else if (statusValue.startsWith("alpha")) {
+ } else if (statusValue.startsWith("dev")) {
statusCode = 1
- } else if (statusValue.startsWith("beta")) {
+ } else if (statusValue.startsWith("alpha")) {
statusCode = 2
- } else if (statusValue.startsWith("rc")) {
+ } else if (statusValue.startsWith("beta")) {
statusCode = 3
- } else if (statusValue.startsWith("stable")) {
+ } else if (statusValue.startsWith("rc")) {
statusCode = 4
+ } else if (statusValue.startsWith("stable")) {
+ statusCode = 5
} else {
statusCode = 0
}
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index 10c28a00b2..f94454e2a7 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -9,7 +9,7 @@ buildscript {
dependencies {
classpath libraries.androidGradlePlugin
classpath libraries.kotlinGradlePlugin
- classpath 'io.github.gradle-nexus:publish-plugin:1.1.0'
+ classpath 'io.github.gradle-nexus:publish-plugin:1.3.0'
}
}
@@ -38,9 +38,7 @@ ext {
supportedAbis = ["arm32", "arm64", "x86_32", "x86_64"]
supportedFlavors = ["editor", "template"]
supportedFlavorsBuildTypes = [
- // 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.
- "editor": ["dev", "debug"],
+ "editor": ["dev", "debug", "release"],
"template": ["dev", "debug", "release"]
]
@@ -54,6 +52,7 @@ ext {
def rootDir = "../../.."
def binDir = "$rootDir/bin/"
+def androidEditorBuildsDir = "$binDir/android_editor_builds/"
def getSconsTaskName(String flavor, String buildType, String abi) {
return "compileGodotNativeLibs" + flavor.capitalize() + buildType.capitalize() + abi.capitalize()
@@ -221,18 +220,46 @@ def isAndroidStudio() {
return sysProps != null && sysProps['idea.platform.prefix'] != null
}
-task copyEditorDebugBinaryToBin(type: Copy) {
+task copyEditorReleaseApkToBin(type: Copy) {
+ dependsOn ':editor:assembleRelease'
+ from('editor/build/outputs/apk/release')
+ into(androidEditorBuildsDir)
+ include('android_editor-release*.apk')
+}
+
+task copyEditorReleaseAabToBin(type: Copy) {
+ dependsOn ':editor:bundleRelease'
+ from('editor/build/outputs/bundle/release')
+ into(androidEditorBuildsDir)
+ include('android_editor-release*.aab')
+}
+
+task copyEditorDebugApkToBin(type: Copy) {
dependsOn ':editor:assembleDebug'
from('editor/build/outputs/apk/debug')
- into(binDir)
- include('android_editor.apk')
+ into(androidEditorBuildsDir)
+ include('android_editor-debug.apk')
}
-task copyEditorDevBinaryToBin(type: Copy) {
+task copyEditorDebugAabToBin(type: Copy) {
+ dependsOn ':editor:bundleDebug'
+ from('editor/build/outputs/bundle/debug')
+ into(androidEditorBuildsDir)
+ include('android_editor-debug.aab')
+}
+
+task copyEditorDevApkToBin(type: Copy) {
dependsOn ':editor:assembleDev'
from('editor/build/outputs/apk/dev')
- into(binDir)
- include('android_editor_dev.apk')
+ into(androidEditorBuildsDir)
+ include('android_editor-dev.apk')
+}
+
+task copyEditorDevAabToBin(type: Copy) {
+ dependsOn ':editor:bundleDev'
+ from('editor/build/outputs/bundle/dev')
+ into(androidEditorBuildsDir)
+ include('android_editor-dev.aab')
}
/**
@@ -253,7 +280,8 @@ task generateGodotEditor {
&& targetLibs.isDirectory()
&& targetLibs.listFiles() != null
&& targetLibs.listFiles().length > 0) {
- tasks += "copyEditor${target.capitalize()}BinaryToBin"
+ tasks += "copyEditor${target.capitalize()}ApkToBin"
+ tasks += "copyEditor${target.capitalize()}AabToBin"
}
}
@@ -301,9 +329,11 @@ task cleanGodotEditor(type: Delete) {
// 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")
+ // Delete the generated aab binaries
+ delete("editor/build/outputs/bundle")
+
+ // Delete the Godot editor apks & aabs in the Godot bin directory
+ delete(androidEditorBuildsDir)
}
/**
diff --git a/platform/android/java/editor/build.gradle b/platform/android/java/editor/build.gradle
index 9152492e9d..38034aa47c 100644
--- a/platform/android/java/editor/build.gradle
+++ b/platform/android/java/editor/build.gradle
@@ -13,22 +13,67 @@ dependencies {
}
ext {
- // Build number added as a suffix to the version code, and incremented for each build/upload to
- // the Google Play store.
- // This should be reset on each stable release of Godot.
- editorBuildNumber = 0
+ // Retrieve the build number from the environment variable; default to 0 if none is specified.
+ // The build number is added as a suffix to the version code for upload to the Google Play store.
+ getEditorBuildNumber = { ->
+ int buildNumber = 0
+ String versionStatus = System.getenv("GODOT_VERSION_STATUS")
+ if (versionStatus != null && !versionStatus.isEmpty()) {
+ try {
+ buildNumber = Integer.parseInt(versionStatus.replaceAll("[^0-9]", ""));
+ } catch (NumberFormatException ignored) {
+ buildNumber = 0
+ }
+ }
+
+ return buildNumber
+ }
// Value by which the Godot version code should be offset by to make room for the build number
editorBuildNumberOffset = 100
+
+ // Return the keystore file used for signing the release build.
+ getGodotKeystoreFile = { ->
+ def keyStore = System.getenv("GODOT_ANDROID_SIGN_KEYSTORE")
+ if (keyStore == null) {
+ return null
+ }
+ return file(keyStore)
+ }
+
+ // Return the key alias used for signing the release build.
+ getGodotKeyAlias = { ->
+ def kAlias = System.getenv("GODOT_ANDROID_KEYSTORE_ALIAS")
+ return kAlias
+ }
+
+ // Return the password for the key used for signing the release build.
+ getGodotSigningPassword = { ->
+ def signingPassword = System.getenv("GODOT_ANDROID_SIGN_PASSWORD")
+ return signingPassword
+ }
+
+ // Returns true if the environment variables contains the configuration for signing the release
+ // build.
+ hasReleaseSigningConfigs = { ->
+ def keystoreFile = getGodotKeystoreFile()
+ def keyAlias = getGodotKeyAlias()
+ def signingPassword = getGodotSigningPassword()
+
+ return keystoreFile != null && keystoreFile.isFile()
+ && keyAlias != null && !keyAlias.isEmpty()
+ && signingPassword != null && !signingPassword.isEmpty()
+ }
}
def generateVersionCode() {
int libraryVersionCode = getGodotLibraryVersionCode()
- return (libraryVersionCode * editorBuildNumberOffset) + editorBuildNumber
+ return (libraryVersionCode * editorBuildNumberOffset) + getEditorBuildNumber()
}
def generateVersionName() {
String libraryVersionName = getGodotLibraryVersionName()
- return libraryVersionName + ".$editorBuildNumber"
+ int buildNumber = getEditorBuildNumber()
+ return buildNumber == 0 ? libraryVersionName : libraryVersionName + ".$buildNumber"
}
android {
@@ -45,6 +90,7 @@ android {
targetSdkVersion versions.targetSdk
missingDimensionStrategy 'products', 'editor'
+ setProperty("archivesBaseName", "android_editor")
}
compileOptions {
@@ -56,6 +102,15 @@ android {
jvmTarget = versions.javaVersion
}
+ signingConfigs {
+ release {
+ storeFile getGodotKeystoreFile()
+ storePassword getGodotSigningPassword()
+ keyAlias getGodotKeyAlias()
+ keyPassword getGodotSigningPassword()
+ }
+ }
+
buildTypes {
dev {
initWith debug
@@ -64,15 +119,14 @@ android {
debug {
initWith release
-
- // Need to swap with the release signing config when this is ready for public release.
+ applicationIdSuffix ".debug"
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.
+ if (hasReleaseSigningConfigs()) {
+ signingConfig signingConfigs.release
+ }
}
}
@@ -82,20 +136,4 @@ android {
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/debug/res/values/strings.xml b/platform/android/java/editor/src/debug/res/values/strings.xml
new file mode 100644
index 0000000000..09ee2d77e1
--- /dev/null
+++ b/platform/android/java/editor/src/debug/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 (debug)</string>
+</resources>
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
index 42ef1436f3..8b6efd572f 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
@@ -113,6 +113,9 @@ open class GodotEditor : FullScreenGodotApp() {
if (args != null && args.isNotEmpty()) {
commandLineParams.addAll(listOf(*args))
}
+ if (BuildConfig.BUILD_TYPE == "dev") {
+ commandLineParams.add("--benchmark")
+ }
}
override fun getCommandLine() = commandLineParams
diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle
index 38133ddd51..4340250ad3 100644
--- a/platform/android/java/lib/build.gradle
+++ b/platform/android/java/lib/build.gradle
@@ -80,19 +80,11 @@ android {
release.jniLibs.srcDirs = ['libs/release']
// Editor jni library
+ editorRelease.jniLibs.srcDirs = ['libs/tools/release']
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 == "") {
@@ -105,9 +97,14 @@ android {
}
boolean devBuild = buildType == "dev"
+ boolean runTests = devBuild
+ boolean productionBuild = !devBuild
+ boolean storeRelease = buildType == "release"
def sconsTarget = flavorName
if (sconsTarget == "template") {
+ // Tests are not supported on template builds
+ runTests = false
switch (buildType) {
case "release":
sconsTarget += "_release"
@@ -135,10 +132,10 @@ android {
def sconsExts = (org.gradle.internal.os.OperatingSystem.current().isWindows()
? [".bat", ".cmd", ".ps1", ".exe"]
: [""])
- logger.lifecycle("Looking for $sconsName executable path")
+ logger.debug("Looking for $sconsName executable path")
for (ext in sconsExts) {
String sconsNameExt = sconsName + ext
- logger.lifecycle("Checking $sconsNameExt")
+ logger.debug("Checking $sconsNameExt")
sconsExecutableFile = org.gradle.internal.os.OperatingSystem.current().findInPath(sconsNameExt)
if (sconsExecutableFile != null) {
// We're done!
@@ -155,7 +152,7 @@ android {
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}")
+ logger.debug("Found executable path for $sconsName: ${sconsExecutableFile.absolutePath}")
}
for (String selectedAbi : selectedAbis) {
@@ -167,7 +164,7 @@ android {
def taskName = getSconsTaskName(flavorName, buildType, selectedAbi)
tasks.create(name: taskName, type: Exec) {
executable sconsExecutableFile.absolutePath
- args "--directory=${pathToRootDir}", "platform=android", "dev_mode=${devBuild}", "dev_build=${devBuild}", "target=${sconsTarget}", "arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors()
+ args "--directory=${pathToRootDir}", "platform=android", "store_release=${storeRelease}", "production=${productionBuild}", "dev_mode=${devBuild}", "dev_build=${devBuild}", "tests=${runTests}", "target=${sconsTarget}", "arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors()
}
// Schedule the tasks so the generated libs are present before the aar file is packaged.
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 99527ccf3a..748a1c41fd 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -39,6 +39,7 @@ import org.godotengine.godot.io.file.FileAccessHandler;
import org.godotengine.godot.plugin.GodotPlugin;
import org.godotengine.godot.plugin.GodotPluginRegistry;
import org.godotengine.godot.tts.GodotTTS;
+import org.godotengine.godot.utils.BenchmarkUtils;
import org.godotengine.godot.utils.GodotNetUtils;
import org.godotengine.godot.utils.PermissionsUtil;
import org.godotengine.godot.xr.XRMode;
@@ -180,7 +181,8 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
public GodotIO io;
public GodotNetUtils netUtils;
public GodotTTS tts;
- DirectoryAccessHandler directoryAccessHandler;
+ private DirectoryAccessHandler directoryAccessHandler;
+ private FileAccessHandler fileAccessHandler;
public interface ResultCallback {
void callback(int requestCode, int resultCode, Intent data);
@@ -274,7 +276,9 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
// ...add to FrameLayout
containerLayout.addView(editText);
- if (!GodotLib.setup(command_line)) {
+ tts = new GodotTTS(activity);
+
+ if (!GodotLib.setup(command_line, tts)) {
Log.e(TAG, "Unable to setup the Godot engine! Aborting...");
alert(R.string.error_engine_setup_message, R.string.text_error_title, this::forceQuit);
return false;
@@ -520,7 +524,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
return cmdline;
} catch (Exception e) {
- e.printStackTrace();
+ // The _cl_ file can be missing with no adverse effect
return new String[0];
}
}
@@ -574,10 +578,9 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
final Activity activity = getActivity();
io = new GodotIO(activity);
netUtils = new GodotNetUtils(activity);
- tts = new GodotTTS(activity);
Context context = getContext();
directoryAccessHandler = new DirectoryAccessHandler(context);
- FileAccessHandler fileAccessHandler = new FileAccessHandler(context);
+ fileAccessHandler = new FileAccessHandler(context);
mSensorManager = (SensorManager)activity.getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mGravity = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
@@ -591,8 +594,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
netUtils,
directoryAccessHandler,
fileAccessHandler,
- use_apk_expansion,
- tts);
+ use_apk_expansion);
result_callback = null;
}
@@ -605,6 +607,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
@Override
public void onCreate(Bundle icicle) {
+ BenchmarkUtils.beginBenchmarkMeasure("Godot::onCreate");
super.onCreate(icicle);
final Activity activity = getActivity();
@@ -653,6 +656,18 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
editor.apply();
i++;
+ } else if (command_line[i].equals("--benchmark")) {
+ BenchmarkUtils.setUseBenchmark(true);
+ new_args.add(command_line[i]);
+ } else if (has_extra && command_line[i].equals("--benchmark-file")) {
+ BenchmarkUtils.setUseBenchmark(true);
+ new_args.add(command_line[i]);
+
+ // Retrieve the filepath
+ BenchmarkUtils.setBenchmarkFile(command_line[i + 1]);
+ new_args.add(command_line[i + 1]);
+
+ i++;
} else if (command_line[i].trim().length() != 0) {
new_args.add(command_line[i]);
}
@@ -723,6 +738,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
mCurrentIntent = activity.getIntent();
initializeGodot();
+ BenchmarkUtils.endBenchmarkMeasure("Godot::onCreate");
}
@Override
@@ -928,20 +944,6 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
// Do something here if sensor accuracy changes.
}
- /*
- @Override public boolean dispatchKeyEvent(KeyEvent event) {
- if (event.getKeyCode()==KeyEvent.KEYCODE_BACK) {
- System.out.printf("** BACK REQUEST!\n");
-
- GodotLib.quit();
- return true;
- }
- System.out.printf("** OTHER KEY!\n");
-
- return false;
- }
- */
-
public void onBackPressed() {
boolean shouldQuit = true;
@@ -1153,10 +1155,35 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
@Keep
+ public DirectoryAccessHandler getDirectoryAccessHandler() {
+ return directoryAccessHandler;
+ }
+
+ @Keep
+ public FileAccessHandler getFileAccessHandler() {
+ return fileAccessHandler;
+ }
+
+ @Keep
private int createNewGodotInstance(String[] args) {
if (godotHost != null) {
return godotHost.onNewGodotInstanceRequested(args);
}
return 0;
}
+
+ @Keep
+ private void beginBenchmarkMeasure(String label) {
+ BenchmarkUtils.beginBenchmarkMeasure(label);
+ }
+
+ @Keep
+ private void endBenchmarkMeasure(String label) {
+ BenchmarkUtils.endBenchmarkMeasure(label);
+ }
+
+ @Keep
+ private void dumpBenchmark(String benchmarkFile) {
+ BenchmarkUtils.dumpBenchmark(fileAccessHandler, benchmarkFile);
+ }
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
index 330e2ede76..b465377743 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
@@ -166,8 +166,10 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
@Override
public void requestPointerCapture() {
- super.requestPointerCapture();
- inputHandler.onPointerCaptureChange(true);
+ if (canCapturePointer()) {
+ super.requestPointerCapture();
+ inputHandler.onPointerCaptureChange(true);
+ }
}
@Override
@@ -188,10 +190,10 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
try {
Bitmap bitmap = null;
if (!TextUtils.isEmpty(imagePath)) {
- if (godot.directoryAccessHandler.filesystemFileExists(imagePath)) {
+ if (godot.getDirectoryAccessHandler().filesystemFileExists(imagePath)) {
// Try to load the bitmap from the file system
bitmap = BitmapFactory.decodeFile(imagePath);
- } else if (godot.directoryAccessHandler.assetsFileExists(imagePath)) {
+ } else if (godot.getDirectoryAccessHandler().assetsFileExists(imagePath)) {
// Try to load the bitmap from the assets directory
AssetManager am = getContext().getAssets();
InputStream imageInputStream = am.open(imagePath);
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
index d9aab950df..c725b1a7c9 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -61,8 +61,7 @@ public class GodotLib {
GodotNetUtils netUtils,
DirectoryAccessHandler directoryAccessHandler,
FileAccessHandler fileAccessHandler,
- boolean use_apk_expansion,
- GodotTTS tts);
+ boolean use_apk_expansion);
/**
* Invoked on the main thread to clean up Godot native layer.
@@ -74,7 +73,7 @@ public class GodotLib {
* Invoked on the GL thread to complete setup for the Godot native layer logic.
* @param p_cmdline Command line arguments used to configure Godot native layer components.
*/
- public static native boolean setup(String[] p_cmdline);
+ public static native boolean setup(String[] p_cmdline, GodotTTS tts);
/**
* Invoked on the GL thread when the underlying Android surface has changed size.
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
index 02c0d67fff..00243dab2a 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
@@ -51,4 +51,8 @@ public interface GodotRenderView {
void configurePointerIcon(int pointerType, String imagePath, float hotSpotX, float hotSpotY);
void setPointerIcon(int pointerType);
+
+ default boolean canCapturePointer() {
+ return getInputHandler().canCapturePointer();
+ }
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
index 34490d4625..681e182adb 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
@@ -134,8 +134,10 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
@Override
public void requestPointerCapture() {
- super.requestPointerCapture();
- mInputHandler.onPointerCaptureChange(true);
+ if (canCapturePointer()) {
+ super.requestPointerCapture();
+ mInputHandler.onPointerCaptureChange(true);
+ }
}
@Override
@@ -162,10 +164,10 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
try {
Bitmap bitmap = null;
if (!TextUtils.isEmpty(imagePath)) {
- if (godot.directoryAccessHandler.filesystemFileExists(imagePath)) {
+ if (godot.getDirectoryAccessHandler().filesystemFileExists(imagePath)) {
// Try to load the bitmap from the file system
bitmap = BitmapFactory.decodeFile(imagePath);
- } else if (godot.directoryAccessHandler.assetsFileExists(imagePath)) {
+ } else if (godot.getDirectoryAccessHandler().assetsFileExists(imagePath)) {
// Try to load the bitmap from the assets directory
AssetManager am = getContext().getAssets();
InputStream imageInputStream = am.open(imagePath);
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
index 7dc5fb6f83..e9bc435689 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
@@ -227,16 +227,14 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
)
dragInProgress = false
}
- return true
}
- dragInProgress = true
-
val x = terminusEvent.x
val y = terminusEvent.y
if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled && !pointerCaptureInProgress) {
GodotLib.pan(x, y, distanceX / 5f, distanceY / 5f)
- } else {
+ } else if (!scaleInProgress){
+ dragInProgress = true
GodotInputHandler.handleMotionEvent(terminusEvent)
}
return true
@@ -246,11 +244,14 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
if (!panningAndScalingEnabled || pointerCaptureInProgress) {
return false
}
- GodotLib.magnify(
- detector.focusX,
- detector.focusY,
- detector.scaleFactor
- )
+
+ if (detector.scaleFactor >= 0.8f && detector.scaleFactor != 1f && detector.scaleFactor <= 1.2f) {
+ GodotLib.magnify(
+ detector.focusX,
+ detector.focusY,
+ detector.scaleFactor
+ )
+ }
return true
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
index cedbbfb7c3..317344f2a5 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
@@ -66,6 +66,11 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
private final ScaleGestureDetector scaleGestureDetector;
private final GodotGestureHandler godotGestureHandler;
+ /**
+ * Used to decide whether mouse capture can be enabled.
+ */
+ private int lastSeenToolType = MotionEvent.TOOL_TYPE_UNKNOWN;
+
public GodotInputHandler(GodotRenderView godotView) {
final Context context = godotView.getView().getContext();
mRenderView = godotView;
@@ -105,6 +110,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
return (source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD;
}
+ public boolean canCapturePointer() {
+ return lastSeenToolType == MotionEvent.TOOL_TYPE_MOUSE;
+ }
+
public void onPointerCaptureChange(boolean hasCapture) {
godotGestureHandler.onPointerCaptureChange(hasCapture);
}
@@ -174,6 +183,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
public boolean onTouchEvent(final MotionEvent event) {
+ lastSeenToolType = event.getToolType(0);
+
this.scaleGestureDetector.onTouchEvent(event);
if (this.gestureDetector.onTouchEvent(event)) {
// The gesture detector has handled the event.
@@ -198,6 +209,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
public boolean onGenericMotionEvent(MotionEvent event) {
+ lastSeenToolType = event.getToolType(0);
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && gestureDetector.onGenericMotionEvent(event)) {
// The gesture detector has handled the event.
return true;
@@ -471,15 +484,27 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative) {
+ // Fix the buttonsMask
+ switch (eventAction) {
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ // Zero-up the button state
+ buttonsMask = 0;
+ break;
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_MOVE:
+ if (buttonsMask == 0) {
+ buttonsMask = MotionEvent.BUTTON_PRIMARY;
+ }
+ break;
+ }
+
// We don't handle ACTION_BUTTON_PRESS and ACTION_BUTTON_RELEASE events as they typically
// follow ACTION_DOWN and ACTION_UP events. As such, handling them would result in duplicate
// stream of events to the engine.
switch (eventAction) {
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
- // Zero-up the button state
- buttonsMask = 0;
- // FALL THROUGH
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_HOVER_ENTER:
case MotionEvent.ACTION_HOVER_EXIT:
diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt
index 357008ca66..984bf607d0 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/io/file/FileAccessHandler.kt
@@ -46,7 +46,7 @@ class FileAccessHandler(val context: Context) {
private val TAG = FileAccessHandler::class.java.simpleName
private const val FILE_NOT_FOUND_ERROR_ID = -1
- private const val INVALID_FILE_ID = 0
+ internal const val INVALID_FILE_ID = 0
private const val STARTING_FILE_ID = 1
internal fun fileExists(context: Context, storageScopeIdentifier: StorageScope.Identifier, path: String?): Boolean {
@@ -96,13 +96,17 @@ class FileAccessHandler(val context: Context) {
private fun hasFileId(fileId: Int) = files.indexOfKey(fileId) >= 0
fun fileOpen(path: String?, modeFlags: Int): Int {
+ val accessFlag = FileAccessFlags.fromNativeModeFlags(modeFlags) ?: return INVALID_FILE_ID
+ return fileOpen(path, accessFlag)
+ }
+
+ internal fun fileOpen(path: String?, accessFlag: FileAccessFlags): Int {
val storageScope = storageScopeIdentifier.identifyStorageScope(path)
if (storageScope == StorageScope.UNKNOWN) {
return INVALID_FILE_ID
}
try {
- val accessFlag = FileAccessFlags.fromNativeModeFlags(modeFlags) ?: return INVALID_FILE_ID
val dataAccess = DataAccess.generateDataAccess(storageScope, context, path!!, accessFlag) ?: return INVALID_FILE_ID
files.put(++lastFileId, dataAccess)
diff --git a/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java b/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java
index ebab8398de..edace53e7f 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/tts/GodotTTS.java
@@ -62,8 +62,9 @@ public class GodotTTS extends UtteranceProgressListener {
final private static int EVENT_CANCEL = 2;
final private static int EVENT_BOUNDARY = 3;
- final private TextToSpeech synth;
- final private LinkedList<GodotUtterance> queue;
+ final private Activity activity;
+ private TextToSpeech synth;
+ private LinkedList<GodotUtterance> queue;
final private Object lock = new Object();
private GodotUtterance lastUtterance;
@@ -71,10 +72,7 @@ public class GodotTTS extends UtteranceProgressListener {
private boolean paused;
public GodotTTS(Activity p_activity) {
- synth = new TextToSpeech(p_activity, null);
- queue = new LinkedList<GodotUtterance>();
-
- synth.setOnUtteranceProgressListener(this);
+ activity = p_activity;
}
private void updateTTS() {
@@ -187,6 +185,16 @@ public class GodotTTS extends UtteranceProgressListener {
}
/**
+ * Initialize synth and query.
+ */
+ public void init() {
+ synth = new TextToSpeech(activity, null);
+ queue = new LinkedList<GodotUtterance>();
+
+ synth.setOnUtteranceProgressListener(this);
+ }
+
+ /**
* Adds an utterance to the queue.
*/
public void speak(String text, String voice, int volume, float pitch, float rate, int utterance_id, boolean interrupt) {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt b/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt
new file mode 100644
index 0000000000..1552c8f082
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/BenchmarkUtils.kt
@@ -0,0 +1,122 @@
+/**************************************************************************/
+/* BenchmarkUtils.kt */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+@file:JvmName("BenchmarkUtils")
+
+package org.godotengine.godot.utils
+
+import android.os.Build
+import android.os.SystemClock
+import android.os.Trace
+import android.util.Log
+import org.godotengine.godot.BuildConfig
+import org.godotengine.godot.io.file.FileAccessFlags
+import org.godotengine.godot.io.file.FileAccessHandler
+import org.json.JSONObject
+import java.nio.ByteBuffer
+import java.util.concurrent.ConcurrentSkipListMap
+
+/**
+ * Contains benchmark related utilities methods
+ */
+private const val TAG = "GodotBenchmark"
+
+var useBenchmark = false
+var benchmarkFile = ""
+
+private val startBenchmarkFrom = ConcurrentSkipListMap<String, Long>()
+private val benchmarkTracker = ConcurrentSkipListMap<String, Double>()
+
+/**
+ * Start measuring and tracing the execution of a given section of code using the given label.
+ *
+ * Must be followed by a call to [endBenchmarkMeasure].
+ *
+ * Note: Only enabled on 'editorDev' build variant.
+ */
+fun beginBenchmarkMeasure(label: String) {
+ if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "dev") {
+ return
+ }
+ startBenchmarkFrom[label] = SystemClock.elapsedRealtime()
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ Trace.beginAsyncSection(label, 0)
+ }
+}
+
+/**
+ * End measuring and tracing of the section of code with the given label.
+ *
+ * Must be preceded by a call [beginBenchmarkMeasure]
+ *
+ * * Note: Only enabled on 'editorDev' build variant.
+ */
+fun endBenchmarkMeasure(label: String) {
+ if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "dev") {
+ return
+ }
+ val startTime = startBenchmarkFrom[label] ?: return
+ val total = SystemClock.elapsedRealtime() - startTime
+ benchmarkTracker[label] = total / 1000.0
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ Trace.endAsyncSection(label, 0)
+ }
+}
+
+/**
+ * Dump the benchmark measurements.
+ * If [filepath] is valid, the data is also written in json format to the specified file.
+ *
+ * * Note: Only enabled on 'editorDev' build variant.
+ */
+@JvmOverloads
+fun dumpBenchmark(fileAccessHandler: FileAccessHandler?, filepath: String? = benchmarkFile) {
+ if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "dev") {
+ return
+ }
+ if (!useBenchmark) {
+ return
+ }
+
+ val printOut =
+ benchmarkTracker.map { "\t- ${it.key} : ${it.value} sec." }.joinToString("\n")
+ Log.i(TAG, "BENCHMARK:\n$printOut")
+
+ if (fileAccessHandler != null && !filepath.isNullOrBlank()) {
+ val fileId = fileAccessHandler.fileOpen(filepath, FileAccessFlags.WRITE)
+ if (fileId != FileAccessHandler.INVALID_FILE_ID) {
+ val jsonOutput = JSONObject(benchmarkTracker.toMap()).toString(4)
+ fileAccessHandler.fileWrite(fileId, ByteBuffer.wrap(jsonOutput.toByteArray()))
+ fileAccessHandler.fileClose(fileId)
+ }
+ }
+}
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 18091649e3..63435853e9 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -116,7 +116,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHei
}
}
-JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion, jobject p_godot_tts) {
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion) {
JavaVM *jvm;
env->GetJavaVM(&jvm);
@@ -133,7 +133,6 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv
DirAccessJAndroid::setup(p_directory_access_handler);
FileAccessFilesystemJAndroid::setup(p_file_access_handler);
NetSocketAndroid::setup(p_net_utils);
- TTS_Android::setup(p_godot_tts);
os_android = new OS_Android(godot_java, godot_io_java, p_use_apk_expansion);
@@ -144,7 +143,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env
_terminate(env, false);
}
-JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline) {
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline, jobject p_godot_tts) {
setup_android_thread();
const char **cmdline = nullptr;
@@ -185,6 +184,8 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env
return false;
}
+ TTS_Android::setup(p_godot_tts);
+
java_class_wrapper = memnew(JavaClassWrapper(godot_java->get_activity()));
GDREGISTER_CLASS(JNISingleton);
return true;
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index 59ab2448d7..9158e89c13 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -37,9 +37,9 @@
// These functions can be called from within JAVA and are the means by which our JAVA implementation calls back into our C++ code.
// See java/src/org/godotengine/godot/GodotLib.java for the JAVA side of this (yes that's why we have the long names)
extern "C" {
-JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion, jobject p_godot_tts);
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz);
-JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline);
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline, jobject p_godot_tts);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jobject p_surface, jint p_width, jint p_height);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface);
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz);
diff --git a/platform/android/java_godot_view_wrapper.cpp b/platform/android/java_godot_view_wrapper.cpp
index b50d4870bd..a95f762e01 100644
--- a/platform/android/java_godot_view_wrapper.cpp
+++ b/platform/android/java_godot_view_wrapper.cpp
@@ -49,6 +49,8 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) {
_request_pointer_capture = env->GetMethodID(_cls, "requestPointerCapture", "()V");
_release_pointer_capture = env->GetMethodID(_cls, "releasePointerCapture", "()V");
}
+
+ _can_capture_pointer = env->GetMethodID(_cls, "canCapturePointer", "()Z");
}
bool GodotJavaViewWrapper::can_update_pointer_icon() const {
@@ -56,7 +58,16 @@ bool GodotJavaViewWrapper::can_update_pointer_icon() const {
}
bool GodotJavaViewWrapper::can_capture_pointer() const {
- return _request_pointer_capture != nullptr && _release_pointer_capture != nullptr;
+ // We can capture the pointer if the other jni capture method ids are initialized,
+ // and GodotView#canCapturePointer() returns true.
+ if (_request_pointer_capture != nullptr && _release_pointer_capture != nullptr && _can_capture_pointer != nullptr) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_NULL_V(env, false);
+
+ return env->CallBooleanMethod(_godot_view, _can_capture_pointer);
+ }
+
+ return false;
}
void GodotJavaViewWrapper::request_pointer_capture() {
diff --git a/platform/android/java_godot_view_wrapper.h b/platform/android/java_godot_view_wrapper.h
index 9b64ded29c..07742d6bb0 100644
--- a/platform/android/java_godot_view_wrapper.h
+++ b/platform/android/java_godot_view_wrapper.h
@@ -44,6 +44,7 @@ private:
jobject _godot_view;
+ jmethodID _can_capture_pointer = 0;
jmethodID _request_pointer_capture = 0;
jmethodID _release_pointer_capture = 0;
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index 2b504ad69b..862d9f0436 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -80,6 +80,9 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
_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;)I");
_get_render_view = p_env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;");
+ _begin_benchmark_measure = p_env->GetMethodID(godot_class, "beginBenchmarkMeasure", "(Ljava/lang/String;)V");
+ _end_benchmark_measure = p_env->GetMethodID(godot_class, "endBenchmarkMeasure", "(Ljava/lang/String;)V");
+ _dump_benchmark = p_env->GetMethodID(godot_class, "dumpBenchmark", "(Ljava/lang/String;)V");
// get some Activity method pointers...
_get_class_loader = p_env->GetMethodID(activity_class, "getClassLoader", "()Ljava/lang/ClassLoader;");
@@ -371,3 +374,30 @@ int GodotJavaWrapper::create_new_godot_instance(List<String> args) {
return 0;
}
}
+
+void GodotJavaWrapper::begin_benchmark_measure(const String &p_label) {
+ if (_begin_benchmark_measure) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_NULL(env);
+ jstring j_label = env->NewStringUTF(p_label.utf8().get_data());
+ env->CallVoidMethod(godot_instance, _begin_benchmark_measure, j_label);
+ }
+}
+
+void GodotJavaWrapper::end_benchmark_measure(const String &p_label) {
+ if (_end_benchmark_measure) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_NULL(env);
+ jstring j_label = env->NewStringUTF(p_label.utf8().get_data());
+ env->CallVoidMethod(godot_instance, _end_benchmark_measure, j_label);
+ }
+}
+
+void GodotJavaWrapper::dump_benchmark(const String &benchmark_file) {
+ if (_dump_benchmark) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_NULL(env);
+ jstring j_benchmark_file = env->NewStringUTF(benchmark_file.utf8().get_data());
+ env->CallVoidMethod(godot_instance, _dump_benchmark, j_benchmark_file);
+ }
+}
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index 05144380e6..245ab33dcf 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -71,6 +71,9 @@ private:
jmethodID _get_class_loader = nullptr;
jmethodID _create_new_godot_instance = nullptr;
jmethodID _get_render_view = nullptr;
+ jmethodID _begin_benchmark_measure = nullptr;
+ jmethodID _end_benchmark_measure = nullptr;
+ jmethodID _dump_benchmark = nullptr;
public:
GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_godot_instance);
@@ -106,6 +109,9 @@ public:
void vibrate(int p_duration_ms);
String get_input_fallback_mapping();
int create_new_godot_instance(List<String> args);
+ void begin_benchmark_measure(const String &p_label);
+ void end_benchmark_measure(const String &p_label);
+ void dump_benchmark(const String &benchmark_file);
};
#endif // JAVA_GODOT_WRAPPER_H
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 73081e35e7..a96dcca3b3 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -182,7 +182,7 @@ String OS_Android::get_name() const {
}
String OS_Android::get_system_property(const char *key) const {
- static String value;
+ String value;
char value_str[PROP_VALUE_MAX];
if (__system_property_get(key, value_str)) {
value = String(value_str);
@@ -230,20 +230,20 @@ String OS_Android::get_version() const {
"ro.potato.version", "ro.xtended.version", "org.evolution.version", "ro.corvus.version", "ro.pa.version",
"ro.crdroid.version", "ro.syberia.version", "ro.arrow.version", "ro.lineage.version" };
for (int i = 0; i < roms.size(); i++) {
- static String rom_version = get_system_property(roms[i]);
+ String rom_version = get_system_property(roms[i]);
if (!rom_version.is_empty()) {
return rom_version;
}
}
- static String mod_version = get_system_property("ro.modversion"); // Handles other Android custom ROMs.
+ String mod_version = get_system_property("ro.modversion"); // Handles other Android custom ROMs.
if (!mod_version.is_empty()) {
return mod_version;
}
// Handles stock Android.
- static String sdk_version = get_system_property("ro.build.version.sdk_int");
- static String build = get_system_property("ro.build.version.incremental");
+ String sdk_version = get_system_property("ro.build.version.sdk_int");
+ String build = get_system_property("ro.build.version.incremental");
if (!sdk_version.is_empty()) {
if (!build.is_empty()) {
return vformat("%s.%s", sdk_version, build);
@@ -675,6 +675,27 @@ String OS_Android::get_config_path() const {
return get_user_data_dir().path_join("config");
}
+void OS_Android::benchmark_begin_measure(const String &p_what) {
+#ifdef TOOLS_ENABLED
+ godot_java->begin_benchmark_measure(p_what);
+#endif
+}
+
+void OS_Android::benchmark_end_measure(const String &p_what) {
+#ifdef TOOLS_ENABLED
+ godot_java->end_benchmark_measure(p_what);
+#endif
+}
+
+void OS_Android::benchmark_dump() {
+#ifdef TOOLS_ENABLED
+ if (!is_use_benchmark_set()) {
+ return;
+ }
+ godot_java->dump_benchmark(get_benchmark_file());
+#endif
+}
+
bool OS_Android::_check_internal_feature_support(const String &p_feature) {
if (p_feature == "system_fonts") {
return true;
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index f1d08b7cfe..99fe501975 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -164,6 +164,10 @@ public:
virtual Error setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path) override;
+ virtual void benchmark_begin_measure(const String &p_what) override;
+ virtual void benchmark_end_measure(const String &p_what) override;
+ virtual void benchmark_dump() 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();
diff --git a/platform/android/tts_android.cpp b/platform/android/tts_android.cpp
index c08c1d4941..aef59c2584 100644
--- a/platform/android/tts_android.cpp
+++ b/platform/android/tts_android.cpp
@@ -35,9 +35,11 @@
#include "string_android.h"
#include "thread_jandroid.h"
+bool TTS_Android::initialized = false;
jobject TTS_Android::tts = nullptr;
jclass TTS_Android::cls = nullptr;
+jmethodID TTS_Android::_init = nullptr;
jmethodID TTS_Android::_is_speaking = nullptr;
jmethodID TTS_Android::_is_paused = nullptr;
jmethodID TTS_Android::_get_voices = nullptr;
@@ -49,23 +51,34 @@ jmethodID TTS_Android::_stop_speaking = nullptr;
HashMap<int, Char16String> TTS_Android::ids;
void TTS_Android::setup(jobject p_tts) {
- JNIEnv *env = get_jni_env();
+ bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");
+ if (tts_enabled) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_COND(env == nullptr);
+
+ tts = env->NewGlobalRef(p_tts);
- tts = env->NewGlobalRef(p_tts);
+ jclass c = env->GetObjectClass(tts);
+ cls = (jclass)env->NewGlobalRef(c);
- jclass c = env->GetObjectClass(tts);
- cls = (jclass)env->NewGlobalRef(c);
+ _init = env->GetMethodID(cls, "init", "()V");
+ _is_speaking = env->GetMethodID(cls, "isSpeaking", "()Z");
+ _is_paused = env->GetMethodID(cls, "isPaused", "()Z");
+ _get_voices = env->GetMethodID(cls, "getVoices", "()[Ljava/lang/String;");
+ _speak = env->GetMethodID(cls, "speak", "(Ljava/lang/String;Ljava/lang/String;IFFIZ)V");
+ _pause_speaking = env->GetMethodID(cls, "pauseSpeaking", "()V");
+ _resume_speaking = env->GetMethodID(cls, "resumeSpeaking", "()V");
+ _stop_speaking = env->GetMethodID(cls, "stopSpeaking", "()V");
- _is_speaking = env->GetMethodID(cls, "isSpeaking", "()Z");
- _is_paused = env->GetMethodID(cls, "isPaused", "()Z");
- _get_voices = env->GetMethodID(cls, "getVoices", "()[Ljava/lang/String;");
- _speak = env->GetMethodID(cls, "speak", "(Ljava/lang/String;Ljava/lang/String;IFFIZ)V");
- _pause_speaking = env->GetMethodID(cls, "pauseSpeaking", "()V");
- _resume_speaking = env->GetMethodID(cls, "resumeSpeaking", "()V");
- _stop_speaking = env->GetMethodID(cls, "stopSpeaking", "()V");
+ if (_init) {
+ env->CallVoidMethod(tts, _init);
+ initialized = true;
+ }
+ }
}
void TTS_Android::_java_utterance_callback(int p_event, int p_id, int p_pos) {
+ ERR_FAIL_COND_MSG(!initialized, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (ids.has(p_id)) {
int pos = 0;
if ((DisplayServer::TTSUtteranceEvent)p_event == DisplayServer::TTS_UTTERANCE_BOUNDARY) {
@@ -86,6 +99,7 @@ void TTS_Android::_java_utterance_callback(int p_event, int p_id, int p_pos) {
}
bool TTS_Android::is_speaking() {
+ ERR_FAIL_COND_V_MSG(!initialized, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (_is_speaking) {
JNIEnv *env = get_jni_env();
@@ -97,6 +111,7 @@ bool TTS_Android::is_speaking() {
}
bool TTS_Android::is_paused() {
+ ERR_FAIL_COND_V_MSG(!initialized, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (_is_paused) {
JNIEnv *env = get_jni_env();
@@ -108,6 +123,7 @@ bool TTS_Android::is_paused() {
}
Array TTS_Android::get_voices() {
+ ERR_FAIL_COND_V_MSG(!initialized, Array(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
Array list;
if (_get_voices) {
JNIEnv *env = get_jni_env();
@@ -135,6 +151,7 @@ Array TTS_Android::get_voices() {
}
void TTS_Android::speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
+ ERR_FAIL_COND_MSG(!initialized, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (p_interrupt) {
stop();
}
@@ -157,6 +174,7 @@ void TTS_Android::speak(const String &p_text, const String &p_voice, int p_volum
}
void TTS_Android::pause() {
+ ERR_FAIL_COND_MSG(!initialized, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (_pause_speaking) {
JNIEnv *env = get_jni_env();
@@ -166,6 +184,7 @@ void TTS_Android::pause() {
}
void TTS_Android::resume() {
+ ERR_FAIL_COND_MSG(!initialized, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
if (_resume_speaking) {
JNIEnv *env = get_jni_env();
@@ -175,6 +194,7 @@ void TTS_Android::resume() {
}
void TTS_Android::stop() {
+ ERR_FAIL_COND_MSG(!initialized, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech.");
for (const KeyValue<int, Char16String> &E : ids) {
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, E.key);
}
diff --git a/platform/android/tts_android.h b/platform/android/tts_android.h
index 8e00ac5000..39efef6ed1 100644
--- a/platform/android/tts_android.h
+++ b/platform/android/tts_android.h
@@ -31,6 +31,7 @@
#ifndef TTS_ANDROID_H
#define TTS_ANDROID_H
+#include "core/config/project_settings.h"
#include "core/string/ustring.h"
#include "core/templates/hash_map.h"
#include "core/variant/array.h"
@@ -39,9 +40,11 @@
#include <jni.h>
class TTS_Android {
+ static bool initialized;
static jobject tts;
static jclass cls;
+ static jmethodID _init;
static jmethodID _is_speaking;
static jmethodID _is_paused;
static jmethodID _get_voices;