summaryrefslogtreecommitdiffstats
path: root/platform/android/java
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android/java')
-rw-r--r--platform/android/java/app/build.gradle68
-rw-r--r--platform/android/java/app/config.gradle16
-rw-r--r--platform/android/java/build.gradle232
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.kt149
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java14
-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.java14
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java184
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/InputEventRunnable.java353
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/io/file/FileData.kt2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/io/file/MediaStoreData.kt2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java4
12 files changed, 653 insertions, 389 deletions
diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle
index 01d5d9ef92..05b4f379b3 100644
--- a/platform/android/java/app/build.gradle
+++ b/platform/android/java/app/build.gradle
@@ -211,70 +211,24 @@ android {
}
}
-task copyAndRenameDebugApk(type: Copy) {
+task copyAndRenameBinary(type: Copy) {
// The 'doNotTrackState' is added to disable gradle's up-to-date checks for output files
// and directories. Otherwise this check may cause permissions access failures on Windows
// machines.
doNotTrackState("No need for up-to-date checks for the copy-and-rename operation")
- from "$buildDir/outputs/apk/debug/android_debug.apk"
- into getExportPath()
- rename "android_debug.apk", getExportFilename()
-}
+ String exportPath = getExportPath()
+ String exportFilename = getExportFilename()
+ String exportBuildType = getExportBuildType()
+ String exportFormat = getExportFormat()
-task copyAndRenameDevApk(type: Copy) {
- // The 'doNotTrackState' is added to disable gradle's up-to-date checks for output files
- // and directories. Otherwise this check may cause permissions access failures on Windows
- // machines.
- doNotTrackState("No need for up-to-date checks for the copy-and-rename operation")
-
- from "$buildDir/outputs/apk/dev/android_dev.apk"
- into getExportPath()
- rename "android_dev.apk", getExportFilename()
-}
-
-task copyAndRenameReleaseApk(type: Copy) {
- // The 'doNotTrackState' is added to disable gradle's up-to-date checks for output files
- // and directories. Otherwise this check may cause permissions access failures on Windows
- // machines.
- doNotTrackState("No need for up-to-date checks for the copy-and-rename operation")
-
- from "$buildDir/outputs/apk/release/android_release.apk"
- into getExportPath()
- rename "android_release.apk", getExportFilename()
-}
-
-task copyAndRenameDebugAab(type: Copy) {
- // The 'doNotTrackState' is added to disable gradle's up-to-date checks for output files
- // and directories. Otherwise this check may cause permissions access failures on Windows
- // machines.
- doNotTrackState("No need for up-to-date checks for the copy-and-rename operation")
-
- from "$buildDir/outputs/bundle/debug/build-debug.aab"
- into getExportPath()
- rename "build-debug.aab", getExportFilename()
-}
-
-task copyAndRenameDevAab(type: Copy) {
- // The 'doNotTrackState' is added to disable gradle's up-to-date checks for output files
- // and directories. Otherwise this check may cause permissions access failures on Windows
- // machines.
- doNotTrackState("No need for up-to-date checks for the copy-and-rename operation")
-
- from "$buildDir/outputs/bundle/dev/build-dev.aab"
- into getExportPath()
- rename "build-dev.aab", getExportFilename()
-}
-
-task copyAndRenameReleaseAab(type: Copy) {
- // The 'doNotTrackState' is added to disable gradle's up-to-date checks for output files
- // and directories. Otherwise this check may cause permissions access failures on Windows
- // machines.
- doNotTrackState("No need for up-to-date checks for the copy-and-rename operation")
+ boolean isAab = exportFormat == "aab"
+ String sourceFilepath = isAab ? "$buildDir/outputs/bundle/$exportBuildType/build-${exportBuildType}.aab" : "$buildDir/outputs/apk/$exportBuildType/android_${exportBuildType}.apk"
+ String sourceFilename = isAab ? "build-${exportBuildType}.aab" : "android_${exportBuildType}.apk"
- from "$buildDir/outputs/bundle/release/build-release.aab"
- into getExportPath()
- rename "build-release.aab", getExportFilename()
+ from sourceFilepath
+ into exportPath
+ rename sourceFilename, exportFilename
}
/**
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index eb9ad9de05..611a9c4a40 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -224,6 +224,22 @@ ext.getExportFilename = {
return exportFilename
}
+ext.getExportBuildType = {
+ String exportBuildType = project.hasProperty("export_build_type") ? project.property("export_build_type") : ""
+ if (exportBuildType == null || exportBuildType.isEmpty()) {
+ exportBuildType = "debug"
+ }
+ return exportBuildType
+}
+
+ext.getExportFormat = {
+ String exportFormat = project.hasProperty("export_format") ? project.property("export_format") : ""
+ if (exportFormat == null || exportFormat.isEmpty()) {
+ exportFormat = "apk"
+ }
+ return exportFormat
+}
+
/**
* Parse the project properties for the 'plugins_maven_repos' property and return the list
* of maven repos.
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index f5555289fd..771bda6948 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -35,116 +35,17 @@ ext {
// `./gradlew generateGodotTemplates` build command instead after running the `scons` command(s).
// The {selectedAbis} values must be from the {supportedAbis} values.
selectedAbis = ["arm64"]
-}
-def rootDir = "../../.."
-def binDir = "$rootDir/bin/"
-def androidEditorBuildsDir = "$binDir/android_editor_builds/"
+ rootDir = "../../.."
+ binDir = "$rootDir/bin/"
+ androidEditorBuildsDir = "$binDir/android_editor_builds/"
+}
def getSconsTaskName(String flavor, String buildType, String abi) {
return "compileGodotNativeLibs" + flavor.capitalize() + buildType.capitalize() + abi.capitalize()
}
/**
- * Copy the generated 'android_debug.apk' binary template into the Godot bin directory.
- * Depends on the app build task to ensure the binary is generated prior to copying.
- */
-task copyDebugBinaryToBin(type: Copy) {
- dependsOn ':app:assembleDebug'
- from('app/build/outputs/apk/debug')
- into(binDir)
- 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.
- */
-task copyReleaseBinaryToBin(type: Copy) {
- dependsOn ':app:assembleRelease'
- from('app/build/outputs/apk/release')
- into(binDir)
- include('android_release.apk')
-}
-
-/**
- * Copy the Godot android library archive debug file into the app module debug libs directory.
- * Depends on the library build task to ensure the AAR file is generated prior to copying.
- */
-task copyDebugAARToAppModule(type: Copy) {
- dependsOn ':lib:assembleTemplateDebug'
- from('lib/build/outputs/aar')
- into('app/libs/debug')
- include('godot-lib.template_debug.aar')
-}
-
-/**
- * Copy the Godot android library archive debug file into the root bin directory.
- * Depends on the library build task to ensure the AAR file is generated prior to copying.
- */
-task copyDebugAARToBin(type: Copy) {
- dependsOn ':lib:assembleTemplateDebug'
- from('lib/build/outputs/aar')
- into(binDir)
- include('godot-lib.template_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.template_debug.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.template_debug.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:assembleTemplateRelease'
- from('lib/build/outputs/aar')
- into('app/libs/release')
- include('godot-lib.template_release.aar')
-}
-
-/**
- * Copy the Godot android library archive release file into the root bin directory.
- * Depends on the library build task to ensure the AAR file is generated prior to copying.
- */
-task copyReleaseAARToBin(type: Copy) {
- dependsOn ':lib:assembleTemplateRelease'
- from('lib/build/outputs/aar')
- into(binDir)
- include('godot-lib.template_release.aar')
-}
-
-/**
* Generate Godot gradle build template by zipping the source files from the app directory, as well
* as the AAR files generated by 'copyDebugAAR', 'copyDevAAR' and 'copyReleaseAAR'.
* The zip file also includes some gradle tools to enable gradle builds from the Godot Editor.
@@ -197,7 +98,7 @@ def generateBuildTasks(String flavor = "template") {
throw new GradleException("Invalid build flavor: $flavor")
}
- def tasks = []
+ def buildTasks = []
// Only build the apks and aar files for which we have native shared libraries unless we intend
// to run the scons build tasks.
@@ -206,72 +107,93 @@ def generateBuildTasks(String flavor = "template") {
String libsDir = isTemplate ? "lib/libs/" : "lib/libs/tools/"
for (String target : supportedFlavorsBuildTypes[flavor]) {
File targetLibs = new File(libsDir + target)
+
+ String targetSuffix = target
+ if (target == "dev") {
+ targetSuffix = "debug.dev"
+ }
+
if (!excludeSconsBuildTasks || (targetLibs != null
&& targetLibs.isDirectory()
&& targetLibs.listFiles() != null
&& targetLibs.listFiles().length > 0)) {
+
String capitalizedTarget = target.capitalize()
if (isTemplate) {
- // Copy the generated aar library files to the build directory.
- tasks += "copy${capitalizedTarget}AARToAppModule"
- // Copy the generated aar library files to the bin directory.
- tasks += "copy${capitalizedTarget}AARToBin"
- // Copy the prebuilt binary templates to the bin directory.
- tasks += "copy${capitalizedTarget}BinaryToBin"
+ // Copy the Godot android library archive file into the app module libs directory.
+ // Depends on the library build task to ensure the AAR file is generated prior to copying.
+ String copyAARTaskName = "copy${capitalizedTarget}AARToAppModule"
+ if (tasks.findByName(copyAARTaskName) != null) {
+ buildTasks += tasks.getByName(copyAARTaskName)
+ } else {
+ buildTasks += tasks.create(name: copyAARTaskName, type: Copy) {
+ dependsOn ":lib:assembleTemplate${capitalizedTarget}"
+ from('lib/build/outputs/aar')
+ include("godot-lib.template_${targetSuffix}.aar")
+ into("app/libs/${target}")
+ }
+ }
+
+ // Copy the Godot android library archive file into the root bin directory.
+ // Depends on the library build task to ensure the AAR file is generated prior to copying.
+ String copyAARToBinTaskName = "copy${capitalizedTarget}AARToBin"
+ if (tasks.findByName(copyAARToBinTaskName) != null) {
+ buildTasks += tasks.getByName(copyAARToBinTaskName)
+ } else {
+ buildTasks += tasks.create(name: copyAARToBinTaskName, type: Copy) {
+ dependsOn ":lib:assembleTemplate${capitalizedTarget}"
+ from('lib/build/outputs/aar')
+ include("godot-lib.template_${targetSuffix}.aar")
+ into(binDir)
+ }
+ }
+
+ // Copy the generated binary template into the Godot bin directory.
+ // Depends on the app build task to ensure the binary is generated prior to copying.
+ String copyBinaryTaskName = "copy${capitalizedTarget}BinaryToBin"
+ if (tasks.findByName(copyBinaryTaskName) != null) {
+ buildTasks += tasks.getByName(copyBinaryTaskName)
+ } else {
+ buildTasks += tasks.create(name: copyBinaryTaskName, type: Copy) {
+ dependsOn ":app:assemble${capitalizedTarget}"
+ from("app/build/outputs/apk/${target}")
+ into(binDir)
+ include("android_${target}.apk")
+ }
+ }
} else {
// Copy the generated editor apk to the bin directory.
- tasks += "copyEditor${capitalizedTarget}ApkToBin"
+ String copyEditorApkTaskName = "copyEditor${capitalizedTarget}ApkToBin"
+ if (tasks.findByName(copyEditorApkTaskName) != null) {
+ buildTasks += tasks.getByName(copyEditorApkTaskName)
+ } else {
+ buildTasks += tasks.create(name: copyEditorApkTaskName, type: Copy) {
+ dependsOn ":editor:assemble${capitalizedTarget}"
+ from("editor/build/outputs/apk/${target}")
+ into(androidEditorBuildsDir)
+ include("android_editor-${target}*.apk")
+ }
+ }
+
// Copy the generated editor aab to the bin directory.
- tasks += "copyEditor${capitalizedTarget}AabToBin"
+ String copyEditorAabTaskName = "copyEditor${capitalizedTarget}AabToBin"
+ if (tasks.findByName(copyEditorAabTaskName) != null) {
+ buildTasks += tasks.getByName(copyEditorAabTaskName)
+ } else {
+ buildTasks += tasks.create(name: copyEditorAabTaskName, type: Copy) {
+ dependsOn ":editor:bundle${capitalizedTarget}"
+ from("editor/build/outputs/bundle/${target}")
+ into(androidEditorBuildsDir)
+ include("android_editor-${target}*.aab")
+ }
+ }
}
} else {
logger.lifecycle("No native shared libs for target $target. Skipping build.")
}
}
- return tasks
-}
-
-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(androidEditorBuildsDir)
- include('android_editor-debug.apk')
-}
-
-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(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')
+ return buildTasks
}
/**
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
index fa39ccb546..111cd48405 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
@@ -39,8 +39,6 @@ import android.content.res.Configuration
import android.content.res.Resources
import android.graphics.Color
import android.hardware.Sensor
-import android.hardware.SensorEvent
-import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.*
import android.util.Log
@@ -53,6 +51,7 @@ import androidx.core.view.WindowInsetsAnimationCompat
import androidx.core.view.WindowInsetsCompat
import com.google.android.vending.expansion.downloader.*
import org.godotengine.godot.input.GodotEditText
+import org.godotengine.godot.input.GodotInputHandler
import org.godotengine.godot.io.directory.DirectoryAccessHandler
import org.godotengine.godot.io.file.FileAccessHandler
import org.godotengine.godot.plugin.GodotPluginRegistry
@@ -73,6 +72,7 @@ import java.io.InputStream
import java.lang.Exception
import java.security.MessageDigest
import java.util.*
+import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference
/**
@@ -81,7 +81,7 @@ import java.util.concurrent.atomic.AtomicReference
* Can be hosted by [Activity], [Fragment] or [Service] android components, so long as its
* lifecycle methods are properly invoked.
*/
-class Godot(private val context: Context) : SensorEventListener {
+class Godot(private val context: Context) {
private companion object {
private val TAG = Godot::class.java.simpleName
@@ -99,15 +99,23 @@ class Godot(private val context: Context) : SensorEventListener {
private val pluginRegistry: GodotPluginRegistry by lazy {
GodotPluginRegistry.getPluginRegistry()
}
+
+ private val accelerometer_enabled = AtomicBoolean(false)
private val mAccelerometer: Sensor? by lazy {
mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
}
+
+ private val gravity_enabled = AtomicBoolean(false)
private val mGravity: Sensor? by lazy {
mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)
}
+
+ private val magnetometer_enabled = AtomicBoolean(false)
private val mMagnetometer: Sensor? by lazy {
mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
}
+
+ private val gyroscope_enabled = AtomicBoolean(false)
private val mGyroscope: Sensor? by lazy {
mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
}
@@ -127,6 +135,7 @@ class Godot(private val context: Context) : SensorEventListener {
val fileAccessHandler = FileAccessHandler(context)
val netUtils = GodotNetUtils(context)
private val commandLineFileParser = CommandLineFileParser()
+ private val godotInputHandler = GodotInputHandler(context, this)
/**
* Task to run when the engine terminates.
@@ -154,6 +163,17 @@ class Godot(private val context: Context) : SensorEventListener {
private var renderViewInitialized = false
private var primaryHost: GodotHost? = null
+ /**
+ * Tracks whether we're in the RESUMED lifecycle state.
+ * See [onResume] and [onPause]
+ */
+ private var resumed = false
+
+ /**
+ * Tracks whether [onGodotSetupCompleted] fired.
+ */
+ private val godotMainLoopStarted = AtomicBoolean(false)
+
var io: GodotIO? = null
private var commandLine : MutableList<String> = ArrayList<String>()
@@ -416,10 +436,10 @@ class Godot(private val context: Context) : SensorEventListener {
if (!meetsVulkanRequirements(activity.packageManager)) {
throw IllegalStateException(activity.getString(R.string.error_missing_vulkan_requirements_message))
}
- GodotVulkanRenderView(host, this)
+ GodotVulkanRenderView(host, this, godotInputHandler)
} else {
// Fallback to openGl
- GodotGLRenderView(host, this, xrMode, useDebugOpengl)
+ GodotGLRenderView(host, this, godotInputHandler, xrMode, useDebugOpengl)
}
if (host == primaryHost) {
@@ -520,23 +540,13 @@ class Godot(private val context: Context) : SensorEventListener {
fun onResume(host: GodotHost) {
Log.v(TAG, "OnResume: $host")
+ resumed = true
if (host != primaryHost) {
return
}
renderView?.onActivityResumed()
- if (mAccelerometer != null) {
- mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME)
- }
- if (mGravity != null) {
- mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_GAME)
- }
- if (mMagnetometer != null) {
- mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME)
- }
- if (mGyroscope != null) {
- mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME)
- }
+ registerSensorsIfNeeded()
if (useImmersive) {
val window = requireActivity().window
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
@@ -551,14 +561,34 @@ class Godot(private val context: Context) : SensorEventListener {
}
}
+ private fun registerSensorsIfNeeded() {
+ if (!resumed || !godotMainLoopStarted.get()) {
+ return
+ }
+
+ if (accelerometer_enabled.get() && mAccelerometer != null) {
+ mSensorManager.registerListener(godotInputHandler, mAccelerometer, SensorManager.SENSOR_DELAY_GAME)
+ }
+ if (gravity_enabled.get() && mGravity != null) {
+ mSensorManager.registerListener(godotInputHandler, mGravity, SensorManager.SENSOR_DELAY_GAME)
+ }
+ if (magnetometer_enabled.get() && mMagnetometer != null) {
+ mSensorManager.registerListener(godotInputHandler, mMagnetometer, SensorManager.SENSOR_DELAY_GAME)
+ }
+ if (gyroscope_enabled.get() && mGyroscope != null) {
+ mSensorManager.registerListener(godotInputHandler, mGyroscope, SensorManager.SENSOR_DELAY_GAME)
+ }
+ }
+
fun onPause(host: GodotHost) {
Log.v(TAG, "OnPause: $host")
+ resumed = false
if (host != primaryHost) {
return
}
renderView?.onActivityPaused()
- mSensorManager.unregisterListener(this)
+ mSensorManager.unregisterListener(godotInputHandler)
for (plugin in pluginRegistry.allPlugins) {
plugin.onMainPause()
}
@@ -659,6 +689,16 @@ class Godot(private val context: Context) : SensorEventListener {
*/
private fun onGodotMainLoopStarted() {
Log.v(TAG, "OnGodotMainLoopStarted")
+ godotMainLoopStarted.set(true)
+
+ accelerometer_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_accelerometer")))
+ gravity_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_gravity")))
+ gyroscope_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_gyroscope")))
+ magnetometer_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_magnetometer")))
+
+ runOnUiThread {
+ registerSensorsIfNeeded()
+ }
for (plugin in pluginRegistry.allPlugins) {
plugin.onGodotMainLoopStarted()
@@ -858,77 +898,6 @@ class Godot(private val context: Context) : SensorEventListener {
}
}
- private fun getRotatedValues(values: FloatArray?): FloatArray? {
- if (values == null || values.size != 3) {
- return null
- }
- val rotatedValues = FloatArray(3)
- when (windowManager.defaultDisplay.rotation) {
- Surface.ROTATION_0 -> {
- rotatedValues[0] = values[0]
- rotatedValues[1] = values[1]
- rotatedValues[2] = values[2]
- }
- Surface.ROTATION_90 -> {
- rotatedValues[0] = -values[1]
- rotatedValues[1] = values[0]
- rotatedValues[2] = values[2]
- }
- Surface.ROTATION_180 -> {
- rotatedValues[0] = -values[0]
- rotatedValues[1] = -values[1]
- rotatedValues[2] = values[2]
- }
- Surface.ROTATION_270 -> {
- rotatedValues[0] = values[1]
- rotatedValues[1] = -values[0]
- rotatedValues[2] = values[2]
- }
- }
- return rotatedValues
- }
-
- override fun onSensorChanged(event: SensorEvent) {
- if (renderView == null) {
- return
- }
-
- val rotatedValues = getRotatedValues(event.values)
-
- when (event.sensor.type) {
- Sensor.TYPE_ACCELEROMETER -> {
- rotatedValues?.let {
- renderView?.queueOnRenderThread {
- GodotLib.accelerometer(-it[0], -it[1], -it[2])
- }
- }
- }
- Sensor.TYPE_GRAVITY -> {
- rotatedValues?.let {
- renderView?.queueOnRenderThread {
- GodotLib.gravity(-it[0], -it[1], -it[2])
- }
- }
- }
- Sensor.TYPE_MAGNETIC_FIELD -> {
- rotatedValues?.let {
- renderView?.queueOnRenderThread {
- GodotLib.magnetometer(-it[0], -it[1], -it[2])
- }
- }
- }
- Sensor.TYPE_GYROSCOPE -> {
- rotatedValues?.let {
- renderView?.queueOnRenderThread {
- GodotLib.gyroscope(it[0], it[1], it[2])
- }
- }
- }
- }
- }
-
- override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
-
/**
* Used by the native code (java_godot_wrapper.h) to vibrate the device.
* @param durationMs
@@ -1063,7 +1032,7 @@ class Godot(private val context: Context) : SensorEventListener {
@Keep
private fun initInputDevices() {
- renderView?.initInputDevices()
+ godotInputHandler.initInputDevices()
}
@Keep
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 7fbdb34047..15a811ce83 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
@@ -83,12 +83,12 @@ class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
private final GodotRenderer godotRenderer;
private final SparseArray<PointerIcon> customPointerIcons = new SparseArray<>();
- public GodotGLRenderView(GodotHost host, Godot godot, XRMode xrMode, boolean useDebugOpengl) {
+ public GodotGLRenderView(GodotHost host, Godot godot, GodotInputHandler inputHandler, XRMode xrMode, boolean useDebugOpengl) {
super(host.getActivity());
this.host = host;
this.godot = godot;
- this.inputHandler = new GodotInputHandler(this);
+ this.inputHandler = inputHandler;
this.godotRenderer = new GodotRenderer();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT));
@@ -102,11 +102,6 @@ class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
}
@Override
- public void initInputDevices() {
- this.inputHandler.initInputDevices();
- }
-
- @Override
public void queueOnRenderThread(Runnable event) {
queueEvent(event);
}
@@ -145,11 +140,6 @@ class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
}
@Override
- public void onBackPressed() {
- godot.onBackPressed();
- }
-
- @Override
public GodotInputHandler getInputHandler() {
return inputHandler;
}
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 19ec0fd1a4..30821eaa8e 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
@@ -37,8 +37,6 @@ import android.view.SurfaceView;
public interface GodotRenderView {
SurfaceView getView();
- void initInputDevices();
-
/**
* Starts the thread that will drive Godot's rendering.
*/
@@ -59,8 +57,6 @@ public interface GodotRenderView {
void onActivityDestroyed();
- void onBackPressed();
-
GodotInputHandler getInputHandler();
void configurePointerIcon(int pointerType, String imagePath, float hotSpotX, float hotSpotY);
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 f4411ddf2c..d5b05913d8 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
@@ -57,12 +57,12 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
private final VkRenderer mRenderer;
private final SparseArray<PointerIcon> customPointerIcons = new SparseArray<>();
- public GodotVulkanRenderView(GodotHost host, Godot godot) {
+ public GodotVulkanRenderView(GodotHost host, Godot godot, GodotInputHandler inputHandler) {
super(host.getActivity());
this.host = host;
this.godot = godot;
- mInputHandler = new GodotInputHandler(this);
+ mInputHandler = inputHandler;
mRenderer = new VkRenderer();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT));
@@ -81,11 +81,6 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
}
@Override
- public void initInputDevices() {
- mInputHandler.initInputDevices();
- }
-
- @Override
public void queueOnRenderThread(Runnable event) {
queueOnVkThread(event);
}
@@ -124,11 +119,6 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
}
@Override
- public void onBackPressed() {
- godot.onBackPressed();
- }
-
- @Override
public GodotInputHandler getInputHandler() {
return mInputHandler;
}
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 273774a33d..fb41cd00c0 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
@@ -32,10 +32,14 @@ package org.godotengine.godot.input;
import static org.godotengine.godot.utils.GLUtils.DEBUG;
+import org.godotengine.godot.Godot;
import org.godotengine.godot.GodotLib;
import org.godotengine.godot.GodotRenderView;
import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
import android.hardware.input.InputManager;
import android.os.Build;
import android.util.Log;
@@ -46,6 +50,10 @@ import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
+import android.view.Surface;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
import java.util.Collections;
import java.util.HashSet;
@@ -54,7 +62,7 @@ import java.util.Set;
/**
* Handles input related events for the {@link GodotRenderView} view.
*/
-public class GodotInputHandler implements InputManager.InputDeviceListener {
+public class GodotInputHandler implements InputManager.InputDeviceListener, SensorEventListener {
private static final String TAG = GodotInputHandler.class.getSimpleName();
private static final int ROTARY_INPUT_VERTICAL_AXIS = 1;
@@ -64,8 +72,9 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
private final SparseArray<Joystick> mJoysticksDevices = new SparseArray<>(4);
private final HashSet<Integer> mHardwareKeyboardIds = new HashSet<>();
- private final GodotRenderView mRenderView;
+ private final Godot godot;
private final InputManager mInputManager;
+ private final WindowManager windowManager;
private final GestureDetector gestureDetector;
private final ScaleGestureDetector scaleGestureDetector;
private final GodotGestureHandler godotGestureHandler;
@@ -77,12 +86,13 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
private int rotaryInputAxis = ROTARY_INPUT_VERTICAL_AXIS;
- public GodotInputHandler(GodotRenderView godotView) {
- final Context context = godotView.getView().getContext();
- mRenderView = godotView;
+ public GodotInputHandler(Context context, Godot godot) {
+ this.godot = godot;
mInputManager = (InputManager)context.getSystemService(Context.INPUT_SERVICE);
mInputManager.registerInputDeviceListener(this, null);
+ windowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
+
this.godotGestureHandler = new GodotGestureHandler(this);
this.gestureDetector = new GestureDetector(context, godotGestureHandler);
this.gestureDetector.setIsLongpressEnabled(false);
@@ -174,7 +184,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
public boolean onKeyDown(final int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
- mRenderView.onBackPressed();
+ godot.onBackPressed();
// press 'back' button should not terminate program
//normal handle 'back' event in game logic
return true;
@@ -507,7 +517,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
return handleTouchEvent(event, eventActionOverride, doubleTap);
}
- private static float getEventTiltX(MotionEvent event) {
+ static float getEventTiltX(MotionEvent event) {
// Orientation is returned as a radian value between 0 to pi clockwise or 0 to -pi counterclockwise.
final float orientation = event.getOrientation();
@@ -520,7 +530,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
return (float)-Math.sin(orientation) * tiltMult;
}
- private static float getEventTiltY(MotionEvent event) {
+ static float getEventTiltY(MotionEvent event) {
// Orientation is returned as a radian value between 0 to pi clockwise or 0 to -pi counterclockwise.
final float orientation = event.getOrientation();
@@ -579,6 +589,11 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) {
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return false;
+ }
+
// Fix the buttonsMask
switch (eventAction) {
case MotionEvent.ACTION_CANCEL:
@@ -594,7 +609,6 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
break;
}
- final int updatedButtonsMask = buttonsMask;
// 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.
@@ -607,11 +621,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_SCROLL: {
- if (shouldDispatchInputToRenderThread()) {
- mRenderView.queueOnRenderThread(() -> GodotLib.dispatchMouseEvent(eventAction, updatedButtonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY));
- } else {
- GodotLib.dispatchMouseEvent(eventAction, updatedButtonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY);
- }
+ runnable.setMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY);
+ dispatchInputEventRunnable(runnable);
return true;
}
}
@@ -627,22 +638,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
boolean handleTouchEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) {
- final int pointerCount = event.getPointerCount();
- if (pointerCount == 0) {
+ if (event.getPointerCount() == 0) {
return true;
}
- final float[] positions = new float[pointerCount * 6]; // pointerId1, x1, y1, pressure1, tiltX1, tiltY1, pointerId2, etc...
-
- for (int i = 0; i < pointerCount; i++) {
- positions[i * 6 + 0] = event.getPointerId(i);
- positions[i * 6 + 1] = event.getX(i);
- positions[i * 6 + 2] = event.getY(i);
- positions[i * 6 + 3] = event.getPressure(i);
- positions[i * 6 + 4] = getEventTiltX(event);
- positions[i * 6 + 5] = getEventTiltY(event);
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return false;
}
- final int actionPointerId = event.getPointerId(event.getActionIndex());
switch (eventActionOverride) {
case MotionEvent.ACTION_DOWN:
@@ -651,11 +654,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN: {
- if (shouldDispatchInputToRenderThread()) {
- mRenderView.queueOnRenderThread(() -> GodotLib.dispatchTouchEvent(eventActionOverride, actionPointerId, pointerCount, positions, doubleTap));
- } else {
- GodotLib.dispatchTouchEvent(eventActionOverride, actionPointerId, pointerCount, positions, doubleTap);
- }
+ runnable.setTouchEvent(event, eventActionOverride, doubleTap);
+ dispatchInputEventRunnable(runnable);
return true;
}
}
@@ -663,58 +663,128 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
void handleMagnifyEvent(float x, float y, float factor) {
- if (shouldDispatchInputToRenderThread()) {
- mRenderView.queueOnRenderThread(() -> GodotLib.magnify(x, y, factor));
- } else {
- GodotLib.magnify(x, y, factor);
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return;
}
+
+ runnable.setMagnifyEvent(x, y, factor);
+ dispatchInputEventRunnable(runnable);
}
void handlePanEvent(float x, float y, float deltaX, float deltaY) {
- if (shouldDispatchInputToRenderThread()) {
- mRenderView.queueOnRenderThread(() -> GodotLib.pan(x, y, deltaX, deltaY));
- } else {
- GodotLib.pan(x, y, deltaX, deltaY);
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return;
}
+
+ runnable.setPanEvent(x, y, deltaX, deltaY);
+ dispatchInputEventRunnable(runnable);
}
private void handleJoystickButtonEvent(int device, int button, boolean pressed) {
- if (shouldDispatchInputToRenderThread()) {
- mRenderView.queueOnRenderThread(() -> GodotLib.joybutton(device, button, pressed));
- } else {
- GodotLib.joybutton(device, button, pressed);
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return;
}
+
+ runnable.setJoystickButtonEvent(device, button, pressed);
+ dispatchInputEventRunnable(runnable);
}
private void handleJoystickAxisEvent(int device, int axis, float value) {
- if (shouldDispatchInputToRenderThread()) {
- mRenderView.queueOnRenderThread(() -> GodotLib.joyaxis(device, axis, value));
- } else {
- GodotLib.joyaxis(device, axis, value);
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return;
}
+
+ runnable.setJoystickAxisEvent(device, axis, value);
+ dispatchInputEventRunnable(runnable);
}
private void handleJoystickHatEvent(int device, int hatX, int hatY) {
- if (shouldDispatchInputToRenderThread()) {
- mRenderView.queueOnRenderThread(() -> GodotLib.joyhat(device, hatX, hatY));
- } else {
- GodotLib.joyhat(device, hatX, hatY);
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return;
}
+
+ runnable.setJoystickHatEvent(device, hatX, hatY);
+ dispatchInputEventRunnable(runnable);
}
private void handleJoystickConnectionChangedEvent(int device, boolean connected, String name) {
- if (shouldDispatchInputToRenderThread()) {
- mRenderView.queueOnRenderThread(() -> GodotLib.joyconnectionchanged(device, connected, name));
- } else {
- GodotLib.joyconnectionchanged(device, connected, name);
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return;
}
+
+ runnable.setJoystickConnectionChangedEvent(device, connected, name);
+ dispatchInputEventRunnable(runnable);
}
void handleKeyEvent(int physicalKeycode, int unicode, int keyLabel, boolean pressed, boolean echo) {
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return;
+ }
+
+ runnable.setKeyEvent(physicalKeycode, unicode, keyLabel, pressed, echo);
+ dispatchInputEventRunnable(runnable);
+ }
+
+ private void dispatchInputEventRunnable(@NonNull InputEventRunnable runnable) {
if (shouldDispatchInputToRenderThread()) {
- mRenderView.queueOnRenderThread(() -> GodotLib.key(physicalKeycode, unicode, keyLabel, pressed, echo));
+ godot.runOnRenderThread(runnable);
} else {
- GodotLib.key(physicalKeycode, unicode, keyLabel, pressed, echo);
+ runnable.run();
+ }
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ final float[] values = event.values;
+ if (values == null || values.length != 3) {
+ return;
}
+
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return;
+ }
+
+ float rotatedValue0 = 0f;
+ float rotatedValue1 = 0f;
+ float rotatedValue2 = 0f;
+ switch (windowManager.getDefaultDisplay().getRotation()) {
+ case Surface.ROTATION_0:
+ rotatedValue0 = values[0];
+ rotatedValue1 = values[1];
+ rotatedValue2 = values[2];
+ break;
+
+ case Surface.ROTATION_90:
+ rotatedValue0 = -values[1];
+ rotatedValue1 = values[0];
+ rotatedValue2 = values[2];
+ break;
+
+ case Surface.ROTATION_180:
+ rotatedValue0 = -values[0];
+ rotatedValue1 = -values[1];
+ rotatedValue2 = values[2];
+ break;
+
+ case Surface.ROTATION_270:
+ rotatedValue0 = values[1];
+ rotatedValue1 = -values[0];
+ rotatedValue2 = values[2];
+ break;
+ }
+
+ runnable.setSensorEvent(event.sensor.getType(), rotatedValue0, rotatedValue1, rotatedValue2);
+ godot.runOnRenderThread(runnable);
}
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/InputEventRunnable.java b/platform/android/java/lib/src/org/godotengine/godot/input/InputEventRunnable.java
new file mode 100644
index 0000000000..a282791b2e
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/InputEventRunnable.java
@@ -0,0 +1,353 @@
+/**************************************************************************/
+/* InputEventRunnable.java */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+package org.godotengine.godot.input;
+
+import org.godotengine.godot.GodotLib;
+
+import android.hardware.Sensor;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.util.Pools;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Used to dispatch input events.
+ *
+ * This is a specialized version of @{@link Runnable} which allows to allocate a finite pool of
+ * objects for input events dispatching, thus avoid the creation (and garbage collection) of
+ * spurious @{@link Runnable} objects.
+ */
+final class InputEventRunnable implements Runnable {
+ private static final String TAG = InputEventRunnable.class.getSimpleName();
+
+ private static final int MAX_TOUCH_POINTER_COUNT = 10; // assuming 10 fingers as max supported concurrent touch pointers
+
+ private static final Pools.Pool<InputEventRunnable> POOL = new Pools.Pool<>() {
+ private static final int MAX_POOL_SIZE = 120 * 10; // up to 120Hz input events rate for up to 5 secs (ANR limit) * 2
+
+ private final ArrayBlockingQueue<InputEventRunnable> queue = new ArrayBlockingQueue<>(MAX_POOL_SIZE);
+ private final AtomicInteger createdCount = new AtomicInteger();
+
+ @Nullable
+ @Override
+ public InputEventRunnable acquire() {
+ InputEventRunnable instance = queue.poll();
+ if (instance == null) {
+ int creationCount = createdCount.incrementAndGet();
+ if (creationCount <= MAX_POOL_SIZE) {
+ instance = new InputEventRunnable(creationCount - 1);
+ }
+ }
+
+ return instance;
+ }
+
+ @Override
+ public boolean release(@NonNull InputEventRunnable instance) {
+ return queue.offer(instance);
+ }
+ };
+
+ @Nullable
+ static InputEventRunnable obtain() {
+ InputEventRunnable runnable = POOL.acquire();
+ if (runnable == null) {
+ Log.w(TAG, "Input event pool is at capacity");
+ }
+ return runnable;
+ }
+
+ /**
+ * Used to track when this instance was created and added to the pool. Primarily used for
+ * debug purposes.
+ */
+ private final int creationRank;
+
+ private InputEventRunnable(int creationRank) {
+ this.creationRank = creationRank;
+ }
+
+ /**
+ * Set of supported input events.
+ */
+ private enum EventType {
+ MOUSE,
+ TOUCH,
+ MAGNIFY,
+ PAN,
+ JOYSTICK_BUTTON,
+ JOYSTICK_AXIS,
+ JOYSTICK_HAT,
+ JOYSTICK_CONNECTION_CHANGED,
+ KEY,
+ SENSOR
+ }
+
+ private EventType currentEventType = null;
+
+ // common event fields
+ private float eventX;
+ private float eventY;
+ private float eventDeltaX;
+ private float eventDeltaY;
+ private boolean eventPressed;
+
+ // common touch / mouse fields
+ private int eventAction;
+ private boolean doubleTap;
+
+ // Mouse event fields and setter
+ private int buttonsMask;
+ private boolean sourceMouseRelative;
+ private float pressure;
+ private float tiltX;
+ private float tiltY;
+ void setMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) {
+ this.currentEventType = EventType.MOUSE;
+ this.eventAction = eventAction;
+ this.buttonsMask = buttonsMask;
+ this.eventX = x;
+ this.eventY = y;
+ this.eventDeltaX = deltaX;
+ this.eventDeltaY = deltaY;
+ this.doubleTap = doubleClick;
+ this.sourceMouseRelative = sourceMouseRelative;
+ this.pressure = pressure;
+ this.tiltX = tiltX;
+ this.tiltY = tiltY;
+ }
+
+ // Touch event fields and setter
+ private int actionPointerId;
+ private int pointerCount;
+ private final float[] positions = new float[MAX_TOUCH_POINTER_COUNT * 6]; // pointerId1, x1, y1, pressure1, tiltX1, tiltY1, pointerId2, etc...
+ void setTouchEvent(MotionEvent event, int eventAction, boolean doubleTap) {
+ this.currentEventType = EventType.TOUCH;
+ this.eventAction = eventAction;
+ this.doubleTap = doubleTap;
+ this.actionPointerId = event.getPointerId(event.getActionIndex());
+ this.pointerCount = Math.min(event.getPointerCount(), MAX_TOUCH_POINTER_COUNT);
+ for (int i = 0; i < pointerCount; i++) {
+ positions[i * 6 + 0] = event.getPointerId(i);
+ positions[i * 6 + 1] = event.getX(i);
+ positions[i * 6 + 2] = event.getY(i);
+ positions[i * 6 + 3] = event.getPressure(i);
+ positions[i * 6 + 4] = GodotInputHandler.getEventTiltX(event);
+ positions[i * 6 + 5] = GodotInputHandler.getEventTiltY(event);
+ }
+ }
+
+ // Magnify event fields and setter
+ private float magnifyFactor;
+ void setMagnifyEvent(float x, float y, float factor) {
+ this.currentEventType = EventType.MAGNIFY;
+ this.eventX = x;
+ this.eventY = y;
+ this.magnifyFactor = factor;
+ }
+
+ // Pan event setter
+ void setPanEvent(float x, float y, float deltaX, float deltaY) {
+ this.currentEventType = EventType.PAN;
+ this.eventX = x;
+ this.eventY = y;
+ this.eventDeltaX = deltaX;
+ this.eventDeltaY = deltaY;
+ }
+
+ // common joystick field
+ private int joystickDevice;
+
+ // Joystick button event fields and setter
+ private int button;
+ void setJoystickButtonEvent(int device, int button, boolean pressed) {
+ this.currentEventType = EventType.JOYSTICK_BUTTON;
+ this.joystickDevice = device;
+ this.button = button;
+ this.eventPressed = pressed;
+ }
+
+ // Joystick axis event fields and setter
+ private int axis;
+ private float value;
+ void setJoystickAxisEvent(int device, int axis, float value) {
+ this.currentEventType = EventType.JOYSTICK_AXIS;
+ this.joystickDevice = device;
+ this.axis = axis;
+ this.value = value;
+ }
+
+ // Joystick hat event fields and setter
+ private int hatX;
+ private int hatY;
+ void setJoystickHatEvent(int device, int hatX, int hatY) {
+ this.currentEventType = EventType.JOYSTICK_HAT;
+ this.joystickDevice = device;
+ this.hatX = hatX;
+ this.hatY = hatY;
+ }
+
+ // Joystick connection changed event fields and setter
+ private boolean connected;
+ private String joystickName;
+ void setJoystickConnectionChangedEvent(int device, boolean connected, String name) {
+ this.currentEventType = EventType.JOYSTICK_CONNECTION_CHANGED;
+ this.joystickDevice = device;
+ this.connected = connected;
+ this.joystickName = name;
+ }
+
+ // Key event fields and setter
+ private int physicalKeycode;
+ private int unicode;
+ private int keyLabel;
+ private boolean echo;
+ void setKeyEvent(int physicalKeycode, int unicode, int keyLabel, boolean pressed, boolean echo) {
+ this.currentEventType = EventType.KEY;
+ this.physicalKeycode = physicalKeycode;
+ this.unicode = unicode;
+ this.keyLabel = keyLabel;
+ this.eventPressed = pressed;
+ this.echo = echo;
+ }
+
+ // Sensor event fields and setter
+ private int sensorType;
+ private float rotatedValue0;
+ private float rotatedValue1;
+ private float rotatedValue2;
+ void setSensorEvent(int sensorType, float rotatedValue0, float rotatedValue1, float rotatedValue2) {
+ this.currentEventType = EventType.SENSOR;
+ this.sensorType = sensorType;
+ this.rotatedValue0 = rotatedValue0;
+ this.rotatedValue1 = rotatedValue1;
+ this.rotatedValue2 = rotatedValue2;
+ }
+
+ @Override
+ public void run() {
+ try {
+ if (currentEventType == null) {
+ Log.w(TAG, "Invalid event type");
+ return;
+ }
+
+ switch (currentEventType) {
+ case MOUSE:
+ GodotLib.dispatchMouseEvent(
+ eventAction,
+ buttonsMask,
+ eventX,
+ eventY,
+ eventDeltaX,
+ eventDeltaY,
+ doubleTap,
+ sourceMouseRelative,
+ pressure,
+ tiltX,
+ tiltY);
+ break;
+
+ case TOUCH:
+ GodotLib.dispatchTouchEvent(
+ eventAction,
+ actionPointerId,
+ pointerCount,
+ positions,
+ doubleTap);
+ break;
+
+ case MAGNIFY:
+ GodotLib.magnify(eventX, eventY, magnifyFactor);
+ break;
+
+ case PAN:
+ GodotLib.pan(eventX, eventY, eventDeltaX, eventDeltaY);
+ break;
+
+ case JOYSTICK_BUTTON:
+ GodotLib.joybutton(joystickDevice, button, eventPressed);
+ break;
+
+ case JOYSTICK_AXIS:
+ GodotLib.joyaxis(joystickDevice, axis, value);
+ break;
+
+ case JOYSTICK_HAT:
+ GodotLib.joyhat(joystickDevice, hatX, hatY);
+ break;
+
+ case JOYSTICK_CONNECTION_CHANGED:
+ GodotLib.joyconnectionchanged(joystickDevice, connected, joystickName);
+ break;
+
+ case KEY:
+ GodotLib.key(physicalKeycode, unicode, keyLabel, eventPressed, echo);
+ break;
+
+ case SENSOR:
+ switch (sensorType) {
+ case Sensor.TYPE_ACCELEROMETER:
+ GodotLib.accelerometer(-rotatedValue0, -rotatedValue1, -rotatedValue2);
+ break;
+
+ case Sensor.TYPE_GRAVITY:
+ GodotLib.gravity(-rotatedValue0, -rotatedValue1, -rotatedValue2);
+ break;
+
+ case Sensor.TYPE_MAGNETIC_FIELD:
+ GodotLib.magnetometer(-rotatedValue0, -rotatedValue1, -rotatedValue2);
+ break;
+
+ case Sensor.TYPE_GYROSCOPE:
+ GodotLib.gyroscope(rotatedValue0, rotatedValue1, rotatedValue2);
+ break;
+ }
+ break;
+ }
+ } finally {
+ recycle();
+ }
+ }
+
+ /**
+ * Release the current instance back to the pool
+ */
+ private void recycle() {
+ currentEventType = null;
+ POOL.release(this);
+ }
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/file/FileData.kt b/platform/android/java/lib/src/org/godotengine/godot/io/file/FileData.kt
index f2c0577c21..d0b8a8dffa 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/io/file/FileData.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/io/file/FileData.kt
@@ -53,7 +53,7 @@ internal class FileData(filePath: String, accessFlag: FileAccessFlags) : DataAcc
fun fileLastModified(filepath: String): Long {
return try {
- File(filepath).lastModified()
+ File(filepath).lastModified() / 1000L
} catch (e: SecurityException) {
0L
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/io/file/MediaStoreData.kt b/platform/android/java/lib/src/org/godotengine/godot/io/file/MediaStoreData.kt
index 5410eed727..146fc04da4 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/io/file/MediaStoreData.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/io/file/MediaStoreData.kt
@@ -203,7 +203,7 @@ internal class MediaStoreData(context: Context, filePath: String, accessFlag: Fi
}
val dataItem = result[0]
- return dataItem.dateModified.toLong()
+ return dataItem.dateModified.toLong() / 1000L
}
fun rename(context: Context, from: String, to: String): Boolean {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
index 711bca02e7..8976dd65db 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
@@ -43,6 +43,7 @@ import androidx.annotation.Nullable;
import java.lang.reflect.Constructor;
import java.util.Collection;
+import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -82,6 +83,9 @@ public final class GodotPluginRegistry {
* Retrieve the full set of loaded plugins.
*/
public Collection<GodotPlugin> getAllPlugins() {
+ if (registry.isEmpty()) {
+ return Collections.emptyList();
+ }
return registry.values();
}