feat: updated godot version

This commit is contained in:
Sara Gerretsen 2026-04-04 19:38:56 +02:00
parent 0c508b0831
commit 42b028dbb5
4694 changed files with 236470 additions and 401376 deletions

View file

@ -48,6 +48,12 @@ android {
buildConfig = true
}
buildTypes {
dev {
initWith debug
}
}
flavorDimensions = ["products"]
productFlavors {
editor {}
@ -73,11 +79,13 @@ android {
sourceSets {
debug.jniLibs.srcDirs = ['libs/debug']
dev.jniLibs.srcDirs = ['libs/dev']
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']
}
libraryVariants.all { variant ->
@ -91,9 +99,9 @@ android {
throw new GradleException("Invalid build type: $buildType")
}
boolean debugBuild = buildType == "debug"
boolean debugSymbols = debugBuild
boolean runTests = debugBuild
boolean devBuild = buildType == "dev"
boolean debugSymbols = devBuild
boolean runTests = devBuild
boolean storeRelease = buildType == "release"
boolean productionBuild = storeRelease
@ -101,13 +109,12 @@ android {
if (sconsTarget == "template") {
// Tests are not supported on template builds
runTests = false
//noinspection GroovyFallthrough
switch (buildType) {
case "release":
sconsTarget += "_release"
break
case "debug":
case "dev":
default:
sconsTarget += "_debug"
break
@ -116,6 +123,9 @@ android {
// Update the name of the generated library
def outputSuffix = "${sconsTarget}"
if (devBuild) {
outputSuffix = "${outputSuffix}.dev"
}
variant.outputs.all { output ->
output.outputFileName = "godot-lib.${outputSuffix}.aar"
}
@ -158,7 +168,7 @@ android {
def taskName = getSconsTaskName(flavorName, buildType, selectedAbi)
tasks.create(name: taskName, type: Exec) {
executable sconsExecutableFile.absolutePath
args "--directory=${pathToRootDir}", "platform=android", "store_release=${storeRelease}", "production=${productionBuild}", "dev_mode=${debugBuild}", "dev_build=${debugBuild}", "debug_symbols=${debugSymbols}", "tests=${runTests}", "target=${sconsTarget}", "arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors()
args "--directory=${pathToRootDir}", "platform=android", "store_release=${storeRelease}", "production=${productionBuild}", "dev_mode=${devBuild}", "dev_build=${devBuild}", "debug_symbols=${debugSymbols}", "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.

View file

@ -43,7 +43,6 @@ import android.hardware.Sensor
import android.hardware.SensorManager
import android.os.*
import android.util.Log
import android.util.Rational
import android.util.TypedValue
import android.view.*
import android.widget.FrameLayout
@ -58,7 +57,6 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import com.google.android.vending.expansion.downloader.*
import org.godotengine.godot.error.Error
import org.godotengine.godot.feature.PictureInPictureProvider
import org.godotengine.godot.input.GodotEditText
import org.godotengine.godot.input.GodotInputHandler
import org.godotengine.godot.io.FilePicker
@ -177,6 +175,7 @@ class Godot private constructor(val context: Context) {
*/
private var renderViewInitialized = false
private var primaryHost: GodotHost? = null
private var currentConfig = context.resources.configuration
/**
* Tracks whether we're in the RESUMED lifecycle state.
@ -198,7 +197,6 @@ class Godot private constructor(val context: Context) {
private var useDebugOpengl = false
private var darkMode = false
private var backgroundColor: Int = Color.BLACK
private var orientation = Configuration.ORIENTATION_UNDEFINED
internal var containerLayout: FrameLayout? = null
var renderView: GodotRenderView? = null
@ -236,9 +234,7 @@ class Godot private constructor(val context: Context) {
Log.v(TAG, "InitEngine with params: $commandLineParams")
val config = context.resources.configuration
darkMode = (config.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
orientation = config.orientation
darkMode = context.resources?.configuration?.uiMode?.and(Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
beginBenchmarkMeasure("Startup", "Godot::initEngine")
try {
@ -569,14 +565,19 @@ class Godot private constructor(val context: Context) {
!isEditorHint() &&
java.lang.Boolean.parseBoolean(GodotLib.getGlobal("display/window/per_pixel_transparency/allowed"))
Log.d(TAG, "Render view should be transparent: $shouldBeTransparent")
renderView = if (usesVulkan()) {
if (meetsVulkanRequirements(context.packageManager)) {
GodotVulkanRenderView(this, godotInputHandler, shouldBeTransparent)
} else if (canFallbackToOpenGL()) {
// Fallback to OpenGl.
GodotGLRenderView(this, godotInputHandler, xrMode, useDebugOpengl, shouldBeTransparent)
} else {
throw IllegalStateException(context.getString(R.string.error_missing_vulkan_requirements_message))
}
val nativeRenderer = getNativeRenderer();
if (nativeRenderer == "vulkan") {
renderView = GodotVulkanRenderView(this, godotInputHandler, shouldBeTransparent)
} else if (nativeRenderer == "opengl3") {
renderView = GodotGLRenderView(this, godotInputHandler, xrMode, useDebugOpengl, shouldBeTransparent)
} else {
throw IllegalStateException("No native renderer is available.")
// Fallback to OpenGl.
GodotGLRenderView(this, godotInputHandler, xrMode, useDebugOpengl, shouldBeTransparent)
}
renderView?.let {
@ -647,6 +648,9 @@ class Godot private constructor(val context: Context) {
})
renderView?.queueOnRenderThread {
for (plugin in pluginRegistry.allPlugins) {
plugin.onRegisterPluginWithGodotNative()
}
setKeepScreenOn(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")))
}
@ -715,13 +719,6 @@ class Godot private constructor(val context: Context) {
}
}
internal fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) {
Log.v(TAG, "onPictureInPictureModeChanged: $isInPictureInPictureMode")
runOnRenderThread {
GodotLib.onPictureInPictureModeChanged(isInPictureInPictureMode)
}
}
fun onPause(host: GodotHost) {
Log.v(TAG, "OnPause: $host")
resumed = false
@ -778,12 +775,12 @@ class Godot private constructor(val context: Context) {
}
}
if (orientation != newConfig.orientation) {
orientation = newConfig.orientation
if (currentConfig.orientation != newConfig.orientation) {
runOnRenderThread {
GodotLib.onOrientationChange(orientation)
GodotLib.onScreenRotationChange(newConfig.orientation)
}
}
currentConfig = newConfig
}
/**
@ -847,7 +844,6 @@ class Godot private constructor(val context: Context) {
}
for (plugin in pluginRegistry.allPlugins) {
plugin.onRegisterPluginWithGodotNative()
plugin.onGodotSetupCompleted()
}
primaryHost?.onGodotSetupCompleted()
@ -936,25 +932,31 @@ class Godot private constructor(val context: Context) {
*/
private fun isOnUiThread() = Looper.myLooper() == Looper.getMainLooper()
/**
* Returns the native rendering driver.
/**
* Returns true if `Vulkan` is used for rendering.
*/
private fun getNativeRenderer(): String {
val rendererInfo = GodotLib.getRendererInfo(meetsVulkanRequirements(context.packageManager))
var renderingDriverChosen = rendererInfo[0]
var renderingDriverOriginal = rendererInfo[1]
var renderingMethod = rendererInfo[2]
var renderingDriverSource = rendererInfo[3]
var renderingMethodSource = rendererInfo[4]
Log.d(TAG, """renderingDevice: ${renderingDriverChosen} (${renderingDriverSource})
renderer: ${renderingMethod} (${renderingMethodSource})""")
if (renderingDriverOriginal == "vulkan" && renderingDriverChosen == "") {
// Throw the exception for the case where Vulkan failed to create and no fallback was available.
throw IllegalStateException(context.getString(R.string.error_missing_vulkan_requirements_message))
private fun usesVulkan(): Boolean {
val rendererInfo = GodotLib.getRendererInfo()
var renderingDeviceSource = "ProjectSettings"
var renderingDevice = rendererInfo[0]
var rendererSource = "ProjectSettings"
var renderer = rendererInfo[1]
val cmdline = commandLine
var index = cmdline.indexOf("--rendering-method")
if (index > -1 && cmdline.size > index + 1) {
rendererSource = "CommandLine"
renderer = cmdline.get(index + 1)
}
return renderingDriverChosen;
index = cmdline.indexOf("--rendering-driver")
if (index > -1 && cmdline.size > index + 1) {
renderingDeviceSource = "CommandLine"
renderingDevice = cmdline.get(index + 1)
}
val result = ("forward_plus" == renderer || "mobile" == renderer) && "vulkan" == renderingDevice
Log.d(TAG, """usesVulkan(): ${result}
renderingDevice: ${renderingDevice} (${renderingDeviceSource})
renderer: ${renderer} (${rendererSource})""")
return result
}
/**
@ -976,8 +978,8 @@ class Godot private constructor(val context: Context) {
Log.w(TAG, "The vulkan hardware level does not meet the minimum requirement: 1")
}
// Check for api version 1.1
return packageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, 0x401000)
// Check for api version 1.0
return packageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, 0x400003)
}
private fun setKeepScreenOn(enabled: Boolean) {
@ -1315,11 +1317,6 @@ class Godot private constructor(val context: Context) {
primaryHost?.onEditorWorkspaceSelected(workspace)
}
@Keep
private fun nativeOnDistractionFreeModeChanged(enabled: Boolean) {
primaryHost?.onDistractionFreeModeChanged(enabled)
}
@Keep
private fun nativeBuildEnvConnect(callback: GodotCallable): Boolean {
try {
@ -1379,52 +1376,4 @@ class Godot private constructor(val context: Context) {
}
}
@Keep
private fun nativeIsPiPModeSupported(): Boolean {
val hostActivity = getActivity()
if (hostActivity is PictureInPictureProvider) {
return hostActivity.isPiPModeSupported()
}
return false
}
@Keep
private fun nativeIsInPiPMode(): Boolean {
val hostActivity = getActivity()
if (hostActivity is GodotActivity) {
return hostActivity.isInPictureInPictureMode
}
return false
}
@Keep
private fun nativeEnterPiPMode() {
val hostActivity = getActivity()
if (hostActivity is PictureInPictureProvider) {
runOnHostThread {
hostActivity.enterPiPMode()
}
}
}
@Keep
private fun nativeSetPiPModeAspectRatio(numerator: Int, denominator: Int) {
val hostActivity = getActivity()
if (hostActivity is GodotActivity) {
runOnHostThread {
hostActivity.updatePiPParams(aspectRatio = Rational(numerator, denominator))
}
}
}
@Keep
private fun nativeSetAutoEnterPiPModeOnBackground(autoEnterPiPOnBackground: Boolean) {
val hostActivity = getActivity()
if (hostActivity is GodotActivity) {
runOnHostThread {
hostActivity.updatePiPParams(enableAutoEnter = autoEnterPiPOnBackground)
}
}
}
}

View file

@ -31,25 +31,17 @@
package org.godotengine.godot
import android.app.Activity
import android.app.PictureInPictureParams
import android.content.ComponentName
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Rect
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.util.Rational
import android.view.View
import androidx.annotation.CallSuper
import androidx.annotation.LayoutRes
import androidx.fragment.app.FragmentActivity
import org.godotengine.godot.feature.PictureInPictureProvider
import org.godotengine.godot.utils.CommandLineFileParser
import org.godotengine.godot.utils.PermissionsUtil
import org.godotengine.godot.utils.ProcessPhoenix
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference
/**
* Base abstract activity for Android apps intending to use Godot as the primary screen.
@ -57,7 +49,7 @@ import java.util.concurrent.atomic.AtomicReference
* Also a reference implementation for how to setup and use the [GodotFragment] fragment
* within an Android app.
*/
abstract class GodotActivity : FragmentActivity(), GodotHost, PictureInPictureProvider {
abstract class GodotActivity : FragmentActivity(), GodotHost {
companion object {
private val TAG = GodotActivity::class.java.simpleName
@ -73,12 +65,6 @@ abstract class GodotActivity : FragmentActivity(), GodotHost, PictureInPicturePr
private final val DEFAULT_WINDOW_ID = 664;
}
/**
* Set to true if the activity should automatically enter picture-in-picture when put in the background.
*/
private val pipAspectRatio = AtomicReference<Rational>()
private val autoEnterPiP = AtomicBoolean(false)
private val gameViewSourceRectHint = Rect()
private val commandLineParams = ArrayList<String>()
/**
* Interaction with the [Godot] object is delegated to the [GodotFragment] class.
@ -153,13 +139,6 @@ abstract class GodotActivity : FragmentActivity(), GodotHost, PictureInPicturePr
.setPrimaryNavigationFragment(godotFragment)
.commitNowAllowingStateLoss()
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val gameView = findViewById<View>(R.id.godot_fragment_container)
gameView?.addOnLayoutChangeListener { v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
gameView.getGlobalVisibleRect(gameViewSourceRectHint)
}
}
}
override fun onNewGodotInstanceRequested(args: Array<String>): Int {
@ -170,7 +149,7 @@ abstract class GodotActivity : FragmentActivity(), GodotHost, PictureInPicturePr
.putExtra(EXTRA_COMMAND_LINE_PARAMS, args)
triggerRebirth(null, intent)
// fake 'process' id returned by create_instance() etc
return DEFAULT_WINDOW_ID
return DEFAULT_WINDOW_ID;
}
protected fun triggerRebirth(bundle: Bundle?, intent: Intent) {
@ -188,15 +167,6 @@ abstract class GodotActivity : FragmentActivity(), GodotHost, PictureInPicturePr
super.onDestroy()
}
override fun onStop() {
super.onStop()
if (isInPictureInPictureMode && !isFinishing) {
// We get in this state when PiP is closed, so we terminate the activity.
finish()
}
}
override fun onGodotForceQuit(instance: Godot) {
runOnUiThread { terminateGodotInstance(instance) }
}
@ -226,31 +196,13 @@ abstract class GodotActivity : FragmentActivity(), GodotHost, PictureInPicturePr
}
}
override fun onGodotSetupCompleted() {
super.onGodotSetupCompleted()
if (isPiPEnabled()) {
try {
// Update the aspect ratio for picture-in-picture mode.
val viewportWidth = Integer.parseInt(GodotLib.getGlobal("display/window/size/viewport_width"))
val viewportHeight = Integer.parseInt(GodotLib.getGlobal("display/window/size/viewport_height"))
pipAspectRatio.set(Rational(viewportWidth, viewportHeight))
} catch (e: NumberFormatException) {
Log.w(TAG, "Unable to parse viewport dimensions.", e)
}
runOnHostThread { updatePiPParams() }
}
}
override fun onNewIntent(newIntent: Intent) {
intent = sanitizeLaunchIntent(newIntent)
super.onNewIntent(intent)
handleStartIntent(intent, false)
}
@CallSuper
protected open fun handleStartIntent(intent: Intent, newLaunch: Boolean) {
private fun handleStartIntent(intent: Intent, newLaunch: Boolean) {
if (!newLaunch) {
val newLaunchRequested = intent.getBooleanExtra(EXTRA_NEW_LAUNCH, false)
if (newLaunchRequested) {
@ -304,56 +256,4 @@ abstract class GodotActivity : FragmentActivity(), GodotHost, PictureInPicturePr
@CallSuper
override fun getCommandLine(): MutableList<String> = commandLineParams
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode)
godot?.onPictureInPictureModeChanged(isInPictureInPictureMode)
}
/**
* Returns true if picture-in-picture (PiP) mode is supported.
*/
override fun isPiPModeSupported() = isPiPEnabled() && packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
/**
* Returns true if the current activity has enabled picture-in-picture in its manifest declaration using
* 'android:supportsPictureInPicture="true"'
*/
protected open fun isPiPEnabled() = false
internal fun updatePiPParams(enableAutoEnter: Boolean = autoEnterPiP.get(), aspectRatio: Rational? = pipAspectRatio.get()) {
if (isPiPModeSupported()) {
autoEnterPiP.set(enableAutoEnter)
pipAspectRatio.set(aspectRatio)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val builder = PictureInPictureParams.Builder()
.setSourceRectHint(gameViewSourceRectHint)
.setAspectRatio(aspectRatio)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
builder.setSeamlessResizeEnabled(false)
.setAutoEnterEnabled(enableAutoEnter)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
builder.setExpandedAspectRatio(aspectRatio)
}
setPictureInPictureParams(builder.build())
}
}
}
override fun enterPiPMode() {
if (isPiPModeSupported()) {
updatePiPParams()
Log.v(TAG, "Entering PiP mode")
enterPictureInPictureMode()
}
}
override fun onUserLeaveHint() {
if (autoEnterPiP.get()) {
enterPiPMode()
}
}
}

View file

@ -496,13 +496,6 @@ public class GodotFragment extends Fragment implements IDownloaderClient, GodotH
}
}
@Override
public void onDistractionFreeModeChanged(Boolean enabled) {
if (parentHost != null) {
parentHost.onDistractionFreeModeChanged(enabled);
}
}
@Override
public BuildProvider getBuildProvider() {
if (parentHost != null) {

View file

@ -153,11 +153,6 @@ public interface GodotHost {
*/
default void onEditorWorkspaceSelected(String workspace) {}
/**
* Triggered when the editor's distraction-free mode changes.
*/
default void onDistractionFreeModeChanged(Boolean enabled) {}
/**
* Runs the specified action on a host provided thread.
*/

View file

@ -192,14 +192,11 @@ public class GodotLib {
/**
* Used to get info about the current rendering system.
*
* @return A String array with three elements:
* [0] Rendering driver name chosen for rendering.
* [1] Rendering driver name chosen before any fallbacks were applied.
* [2] Rendering method.
* [3] Source where the rendering driver was chosen from.
* [4] Source where the rendering method was chosen from.
* @return A String array with two elements:
* [0] Rendering driver name.
* [1] Rendering method.
*/
public static native String[] getRendererInfo(boolean p_vulkan_requirements_met);
public static native String[] getRendererInfo();
/**
* Used to access Godot's editor settings.
@ -302,7 +299,7 @@ public class GodotLib {
* Invoked when the screen orientation changes.
* @param orientation the new screen orientation
*/
static native void onOrientationChange(int orientation);
static native void onScreenRotationChange(int orientation);
/**
* @return true if input must be dispatched from the render thread. If false, input is
@ -320,6 +317,4 @@ public class GodotLib {
static native boolean isProjectManagerHint();
static native boolean hasFeature(String feature);
static native void onPictureInPictureModeChanged(boolean isInPictureInPictureMode);
}

View file

@ -38,7 +38,4 @@ package org.godotengine.godot.editor.utils
object EditorUtils {
@JvmStatic
external fun runScene(scene: String, sceneArgs: Array<String>)
@JvmStatic
external fun toggleTitleBar(visible: Boolean)
}

View file

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

View file

@ -57,7 +57,7 @@ public class GodotEditText extends EditText {
private final static int HANDLER_OPEN_IME_KEYBOARD = 2;
private final static int HANDLER_CLOSE_IME_KEYBOARD = 3;
// Enum must be kept up-to-date with DisplayServerEnums::VirtualKeyboardType
// Enum must be kept up-to-date with DisplayServer::VirtualKeyboardType
public enum VirtualKeyboardType {
KEYBOARD_TYPE_DEFAULT,
KEYBOARD_TYPE_MULTILINE,

View file

@ -57,11 +57,6 @@ internal class GodotGestureHandler(private val inputHandler: GodotInputHandler)
var scrollDeadzoneDisabled = false
/**
* Enable haptic feedback on long-press right-click
*/
var hapticFeedbackEnabled = false
private var nextDownIsDoubleTap = false
private var dragInProgress = false
private var scaleInProgress = false
@ -85,9 +80,6 @@ internal class GodotGestureHandler(private val inputHandler: GodotInputHandler)
override fun onLongPress(event: MotionEvent) {
val toolType = GodotInputHandler.getEventToolType(event)
if (toolType != MotionEvent.TOOL_TYPE_MOUSE) {
if (hapticFeedbackEnabled) {
inputHandler.performHapticFeedback()
}
contextClickRouter(event)
}
}

View file

@ -47,7 +47,6 @@ import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.GestureDetector;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
@ -134,23 +133,6 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens
this.godotGestureHandler.setScrollDeadzoneDisabled(disable);
}
/**
* Enable haptic feedback (vibration) when a long-press right-click is triggered.
*/
public void enableHapticFeedback(boolean enable) {
this.godotGestureHandler.setHapticFeedbackEnabled(enable);
}
/**
* Perform haptic feedback on the render view.
*/
void performHapticFeedback() {
GodotRenderView view = godot.getRenderView();
if (view != null) {
view.getView().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
}
}
/**
* Enable multi-fingers pan & scale gestures. This is false by default.
* <p>

View file

@ -203,10 +203,8 @@ internal abstract class DataAccess {
abstract fun write(buffer: ByteBuffer): Boolean
fun seekFromEnd(positionFromEnd: Long) {
val positionFromBeginning = size() + positionFromEnd
if (positionFromBeginning >= 0) {
seek(positionFromBeginning)
}
val positionFromBeginning = max(0, size() - positionFromEnd)
seek(positionFromBeginning)
}
abstract class FileChannelDataAccess(private val filePath: String) : DataAccess() {

View file

@ -32,13 +32,10 @@ package org.godotengine.godot.plugin
import android.content.Intent
import android.util.Log
import androidx.annotation.Keep
import androidx.core.net.toUri
import org.godotengine.godot.Godot
import org.godotengine.godot.variant.Callable
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Proxy
/**
* Built-in Godot Android plugin used to provide access to the Android runtime capabilities.
@ -46,76 +43,7 @@ import java.lang.reflect.Proxy
* @see <a href="https://docs.godotengine.org/en/latest/tutorials/platform/android/javaclasswrapper_and_androidruntimeplugin.html">Integrating with Android APIs</a>
*/
class AndroidRuntimePlugin(godot: Godot) : GodotPlugin(godot) {
companion object {
private val TAG = AndroidRuntimePlugin::class.java.simpleName
/**
* Helper method used to generate Godot Proxy instances.
*/
@JvmStatic
@Keep
private fun generateProxyInstance(interfaces: Array<String>, invocationHandler: InvocationHandler): Any? {
try {
val interfaceClasses = interfaces.map { Class.forName(it) }.toTypedArray()
val proxy = Proxy.newProxyInstance(invocationHandler.javaClass.classLoader, interfaceClasses, invocationHandler)
return proxy
} catch (e: Exception) {
Log.w(TAG, "Error generating Godot proxy for interfaces ${interfaces.joinToString(",")}", e)
}
return null
}
/**
* Utility method used to create [java.lang.reflect.Proxy] instance wrapping a given Godot [Callable].
*
* The [Proxy] instance is used to implement one SAM interface with the [Callable] serving as the delegate
* implementation for the SAM interface overridden methods.
*/
@JvmStatic
@Keep
private fun createProxyFromGodotCallable(interfaceName: String, godotCallable: Callable): Any? {
return generateProxyInstance(arrayOf(interfaceName)) { proxy, method, args ->
when (method.name) {
// We automatically handle 'toString', 'equals' and 'hashCode' to simplify the task of the caller
// and provide consistency.
"toString" -> "Godot Callable Proxy for $interfaceName"
"equals" -> proxy == args[0]
"hashCode" -> godotCallable.hashCode()
// Invocation for the interface single abstract method falls here and is dispatched to the
// Godot [Callable].
else -> godotCallable.call(*args)
}
}
}
/**
* Utility method used to create [java.lang.reflect.Proxy] instance wrapping a given Godot Object represented by
* its ObjectID.
*
* The [Proxy] instance is used to implement one or multiple interfaces with the Object represented by
* [godotObjectID] serving as the delegate implementation for the interface(s) overridden methods.
*/
@JvmStatic
@Keep
private fun createProxyFromGodotObjectID(godotObjectID: Long, interfaces: Array<String>): Any? {
return generateProxyInstance(interfaces) { proxy, method, args ->
when (val methodName = method.name) {
// We automatically handle 'toString', 'equals' and 'hashCode' to simplify the task of the caller
// and provide consistency.
"toString" -> "Godot Object Proxy for ${interfaces.joinToString(",")}"
"equals" -> proxy == args[0]
"hashCode" -> godotObjectID
// Invocation for the remaining interface(s) methods falls here and is dispatched to the
// Godot Object.
else -> Callable.call(godotObjectID, methodName, *args)
}
}
}
}
private val TAG = AndroidRuntimePlugin::class.java.simpleName
override fun getPluginName() = "AndroidRuntime"

View file

@ -57,7 +57,7 @@ import java.util.Set;
*/
@Keep
public class GodotTTS extends UtteranceProgressListener implements TextToSpeech.OnInitListener {
// Note: These constants must be in sync with DisplayServerEnums::TTSUtteranceEvent enum from "servers/display/display_server.h".
// Note: These constants must be in sync with DisplayServer::TTSUtteranceEvent enum from "servers/display/display_server.h".
final private static int EVENT_START = 0;
final private static int EVENT_END = 1;
final private static int EVENT_CANCEL = 2;

View file

@ -64,7 +64,7 @@ private val benchmarkTracker = Collections.synchronizedMap(LinkedHashMap<Pair<St
* Note: Only enabled on 'editorDev' build variant.
*/
fun beginBenchmarkMeasure(scope: String, label: String) {
if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "debug") {
if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "dev") {
return
}
val key = Pair(scope, label)
@ -84,7 +84,7 @@ fun beginBenchmarkMeasure(scope: String, label: String) {
*/
@JvmOverloads
fun endBenchmarkMeasure(scope: String, label: String, dumpBenchmark: Boolean = false) {
if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "debug") {
if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "dev") {
return
}
val key = Pair(scope, label)
@ -109,7 +109,7 @@ fun endBenchmarkMeasure(scope: String, label: String, dumpBenchmark: Boolean = f
*/
@JvmOverloads
fun dumpBenchmark(fileAccessHandler: FileAccessHandler? = null, filepath: String? = benchmarkFile) {
if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "debug") {
if (BuildConfig.FLAVOR != "editor" || BuildConfig.BUILD_TYPE != "dev") {
return
}
if (!useBenchmark || benchmarkTracker.isEmpty()) {