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

@ -27,6 +27,8 @@ allprojects {
}
configurations {
// Initializes a placeholder for the devImplementation dependency configuration.
devImplementation {}
// Initializes a placeholder for the monoImplementation dependency configuration.
monoImplementation {}
}
@ -51,32 +53,15 @@ dependencies {
// Godot gradle build mode. In this scenario this project is the only one around and the Godot
// library is available through the pre-generated godot-lib.*.aar android archive files.
debugImplementation fileTree(dir: 'libs/debug', include: ['**/*.jar', '*.aar'])
devImplementation fileTree(dir: 'libs/dev', include: ['**/*.jar', '*.aar'])
releaseImplementation fileTree(dir: 'libs/release', include: ['**/*.jar', '*.aar'])
}
// Godot user plugins remote dependencies
String[] remoteDeps = getGodotPluginsRemoteBinaries()
if (remoteDeps != null && remoteDeps.size() > 0) {
def platformPattern = /^\s*(platform|enforcedPlatform)\s*\(\s*['"]*(\S+)['"]*\s*\)$/
for (String dep : remoteDeps) {
def matcher = dep =~ platformPattern
if (matcher) {
switch (matcher[0][1]) {
case "platform":
implementation platform(matcher[0][2])
break
case "enforcedPlatform":
implementation enforcedPlatform(matcher[0][2])
break
default:
throw new GradleException("Invalid remote platform dependency: $dep")
break
}
} else {
implementation dep
}
implementation dep
}
}
@ -221,6 +206,18 @@ android {
}
}
dev {
initWith debug
// Signing and zip-aligning are skipped for prebuilt builds, but
// performed for Godot gradle builds.
zipAlignEnabled shouldZipAlign()
if (shouldSign()) {
signingConfig signingConfigs.debug
} else {
signingConfig null
}
}
release {
// Signing and zip-aligning are skipped for prebuilt builds, but
// performed for Godot gradle builds.
@ -254,6 +251,7 @@ android {
sourceSets {
main.res.srcDirs += ['res']
debug.jniLibs.srcDirs = ['libs/debug', 'libs/debug/vulkan_validation_layers']
dev.jniLibs.srcDirs = ['libs/dev']
release.jniLibs.srcDirs = ['libs/release']
}
@ -332,6 +330,9 @@ module, so we're ensuring the ':app:preBuild' task is set to run after those tas
if (rootProject.tasks.findByPath("copyDebugAARToAppModule") != null) {
preBuild.mustRunAfter(rootProject.tasks.named("copyDebugAARToAppModule"))
}
if (rootProject.tasks.findByPath("copyDevAARToAppModule") != null) {
preBuild.mustRunAfter(rootProject.tasks.named("copyDevAARToAppModule"))
}
if (rootProject.tasks.findByPath("copyReleaseAARToAppModule") != null) {
preBuild.mustRunAfter(rootProject.tasks.named("copyReleaseAARToAppModule"))
}

View file

@ -1,23 +1,23 @@
ext.versions = [
androidGradlePlugin: '8.6.1',
compileSdk : 36,
compileSdk : 35,
// Also update:
// - 'platform/android/export/export_plugin.cpp#DEFAULT_MIN_SDK_VERSION'
// - 'platform/android/detect.py#get_min_target_api()'
minSdk : 24,
// Also update 'platform/android/export/export_plugin.cpp#DEFAULT_TARGET_SDK_VERSION'
targetSdk : 36,
buildTools : '36.1.0',
kotlinVersion : '2.1.21',
targetSdk : 35,
buildTools : '35.0.1',
kotlinVersion : '2.1.20',
fragmentVersion : '1.8.6',
nexusPublishVersion: '1.3.0',
javaVersion : JavaVersion.VERSION_17,
// Also update 'platform/android/detect.py#get_ndk_version()' when this is updated.
ndkVersion : '29.0.14206865',
ndkVersion : '28.1.13356709',
splashscreenVersion: '1.0.1',
// 'openxrLoaderVersion' should be set to XR_CURRENT_API_VERSION, see 'thirdparty/openxr'
openxrLoaderVersion: '1.1.53',
openxrVendorsVersion: '4.3.0-stable',
openxrVendorsVersion: '4.2.2-stable',
junitVersion : '1.3.0',
espressoCoreVersion: '3.7.0',
kotlinTestVersion : '1.3.11',

View file

@ -1,4 +1,12 @@
list=[{
"base": &"RefCounted",
"class": &"BaseTest",
"icon": "",
"is_abstract": true,
"is_tool": false,
"language": &"GDScript",
"path": "res://test/base_test.gd"
}, {
"base": &"BaseTest",
"class": &"FileAccessTests",
"icon": "",
@ -14,12 +22,4 @@ list=[{
"is_tool": false,
"language": &"GDScript",
"path": "res://test/javaclasswrapper/java_class_wrapper_tests.gd"
}, {
"base": &"RefCounted",
"class": &"BaseTest",
"icon": "",
"is_abstract": true,
"is_tool": false,
"language": &"GDScript",
"path": "res://test/base_test.gd"
}]

View file

@ -1,29 +1,29 @@
[gd_scene format=3 uid="uid://cg3hylang5fxn"]
[gd_scene load_steps=2 format=3 uid="uid://cg3hylang5fxn"]
[ext_resource type="Script" uid="uid://bv6y7in6otgcm" path="res://main.gd" id="1_j0gfq"]
[node name="Main" type="Node2D" unique_id=852911723]
[node name="Main" type="Node2D"]
script = ExtResource("1_j0gfq")
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=1839352715]
[node name="VBoxContainer" type="VBoxContainer" parent="."]
offset_left = 68.0
offset_top = 102.0
offset_right = 506.0
offset_bottom = 408.0
theme_override_constants/separation = 25
[node name="PluginToastButton" type="Button" parent="VBoxContainer" unique_id=1670434164]
[node name="PluginToastButton" type="Button" parent="VBoxContainer"]
custom_minimum_size = Vector2(0, 50)
layout_mode = 2
text = "Plugin Toast
"
[node name="VibrationButton" type="Button" parent="VBoxContainer" unique_id=648980813]
[node name="VibrationButton" type="Button" parent="VBoxContainer"]
custom_minimum_size = Vector2(0, 50)
layout_mode = 2
text = "Vibration"
[node name="GDScriptToastButton" type="Button" parent="VBoxContainer" unique_id=95554078]
[node name="GDScriptToastButton" type="Button" parent="VBoxContainer"]
custom_minimum_size = Vector2(0, 50)
layout_mode = 2
text = "GDScript Toast

View file

@ -8,15 +8,11 @@
config_version=5
[animation]
compatibility/default_parent_skeleton_in_mesh_instance_3d=true
[application]
config/name="Godot App Instrumentation Tests"
run/main_scene="res://main.tscn"
config/features=PackedStringArray("4.6", "GL Compatibility")
config/features=PackedStringArray("4.5", "GL Compatibility")
config/icon="res://icon.svg"
[debug]

View file

@ -20,10 +20,6 @@ func run_tests():
__exec_test(test_callable)
__exec_test(test_interface_callable_proxy)
__exec_test(test_interface_object_proxy)
print("JavaClassWrapper tests finished.")
print("Tests started: " + str(_test_started))
print("Tests completed: " + str(_test_completed))
@ -173,34 +169,3 @@ func test_callable() -> bool:
assert_equal(cb1_data['called'], true)
return true
func test_interface_callable_proxy() -> bool:
var cb1_data := {called = false, content = ""}
var cb1 = func (content: String) -> void:
cb1_data['called'] = true
cb1_data['content'] = content
var printer_proxy = JavaClassWrapper.create_sam_callback("android.util.Printer", cb1)
assert_true(printer_proxy != null)
printer_proxy.println("This is a callback test")
assert_equal(cb1_data['called'], true)
assert_equal(cb1_data['content'], "This is a callback test")
return true
class PrintProxy:
var test_data := {called = false, content = ""}
func println(content: String) -> void:
test_data['called'] = true
test_data['content'] = content
func test_interface_object_proxy() -> bool:
var print_object = PrintProxy.new()
var proxy = JavaClassWrapper.create_proxy(print_object, ["android.util.Printer"])
assert_true(proxy != null)
proxy.println("This is proxy test")
assert_equal(print_object.test_data['called'], true)
assert_equal(print_object.test_data['content'], "This is proxy test")
return true

View file

@ -35,7 +35,6 @@
android:launchMode="singleInstancePerTask"
android:excludeFromRecents="false"
android:exported="false"
android:supportsPictureInPicture="true"
android:screenOrientation="landscape"
android:windowSoftInputMode="adjustResize"
android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"

View file

@ -92,9 +92,4 @@ public class GodotApp extends GodotActivity {
super.onGodotForceQuit(instance);
}
}
@Override
protected boolean isPiPEnabled() {
return true;
}
}

View file

@ -27,8 +27,8 @@ ext {
supportedFlavors = ["editor", "template"]
supportedAndroidDistributions = ["android", "horizonos", "picoos"]
supportedFlavorsBuildTypes = [
"editor": ["debug", "release"],
"template": ["debug", "release"]
"editor": ["dev", "debug", "release"],
"template": ["dev", "debug", "release"]
]
supportedEditions = ["standard", "mono"]
@ -50,11 +50,11 @@ def getSconsTaskName(String flavor, String buildType, String abi) {
/**
* Generate Godot gradle build template by zipping the source files from the app directory, as well
* as the AAR files generated by 'copyDebugAAR' and 'copyReleaseAAR'.
* as the AAR files generated by 'copyDebugAAR', 'copyDevAAR' and 'copyReleaseAAR'.
* The zip file also includes some gradle tools to enable gradle builds from the Godot Editor.
*/
task zipGradleBuild(type: Zip) {
onlyIf { generateGodotTemplates.state.executed || generateGodotMonoTemplates.state.executed }
onlyIf { generateGodotTemplates.state.executed || generateGodotMonoTemplates.state.executed || generateDevTemplate.state.executed }
doFirst {
logger.lifecycle("Generating Godot gradle build template")
}
@ -124,6 +124,9 @@ def generateBuildTasks(String flavor = "template", String edition = "standard",
File targetLibs = new File(libsDir + target)
String targetSuffix = target
if (target == "dev") {
targetSuffix = "debug.dev"
}
if (!excludeSconsBuildTasks || (targetLibs != null
&& targetLibs.isDirectory()
@ -312,20 +315,26 @@ task cleanGodotTemplates(type: Delete) {
// Delete the Godot templates in the Godot bin directory
delete("$binDir/android_debug.apk")
delete("$binDir/android_dev.apk")
delete("$binDir/android_release.apk")
delete("$binDir/android_monoDebug.apk")
delete("$binDir/android_monoDev.apk")
delete("$binDir/android_monoRelease.apk")
delete("$binDir/android_source.zip")
delete("$binDir/godot-lib.template_debug.aar")
delete("$binDir/godot-lib.template_debug.dev.aar")
delete("$binDir/godot-lib.template_release.aar")
// Cover deletion for the libs using the previous naming scheme
delete("$binDir/godot-lib.debug.aar")
delete("$binDir/godot-lib.dev.aar")
delete("$binDir/godot-lib.release.aar")
// Delete the native debug symbols files.
delete("$binDir/android-editor-debug-native-symbols.zip")
delete("$binDir/android-editor-dev-native-symbols.zip")
delete("$binDir/android-editor-release-native-symbols.zip")
delete("$binDir/android-template-debug-native-symbols.zip")
delete("$binDir/android-template-dev-native-symbols.zip")
delete("$binDir/android-template-release-native-symbols.zip")
}

View file

@ -81,7 +81,7 @@ android {
applicationId "org.godotengine.editor.v4"
versionCode generateVersionCode()
versionName generateVersionName()
minSdkVersion 29 // Minimum recommended sdk version for Vulkan 1.1 support. See https://developer.android.com/games/develop/vulkan/native-engine-support#recommendations
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
missingDimensionStrategy 'products', 'editor'
@ -131,7 +131,14 @@ android {
}
buildTypes {
dev {
initWith debug
applicationIdSuffix ".dev"
manifestPlaceholders += [editorBuildSuffix: " (dev)"]
}
debug {
initWith release
applicationIdSuffix ".debug"
manifestPlaceholders += [editorBuildSuffix: " (debug)"]
signingConfig signingConfigs.debug
@ -140,14 +147,6 @@ android {
release {
if (hasReleaseSigningConfigs()) {
signingConfig signingConfigs.release
} else {
// We default to the debug signingConfigs when the release signing configs are not
// available (e.g: development in Android Studio).
signingConfig signingConfigs.debug
// In addition, we update the application ID to allow installing an Android studio release build
// side by side with a production build from the store.
applicationIdSuffix ".release"
manifestPlaceholders += [editorBuildSuffix: " (release)"]
}
}
}
@ -187,6 +186,7 @@ android {
}
applicationIdSuffix ".pico"
versionNameSuffix "-pico"
minSdkVersion 29
targetSdkVersion 32
}
}

View file

@ -16,14 +16,6 @@
<uses-feature
android:glEsVersion="0x00030000"
android:required="true" />
<uses-feature
android:name="android.hardware.vulkan.version"
android:required="true"
android:version="0x401000" />
<uses-feature
android:name="android.hardware.vulkan.level"
android:required="true"
android:version="1" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
@ -60,7 +52,7 @@
android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
android:exported="false"
android:icon="@mipmap/themed_icon"
android:launchMode="singleInstancePerTask"
android:launchMode="singleTask"
android:screenOrientation="userLandscape">
<layout
android:defaultWidth="@dimen/editor_default_window_width"
@ -93,15 +85,6 @@
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="*/*" />
<data android:host="*" />
<data android:pathPattern=".*\\.godot" />
</intent-filter>
</activity-alias>
<activity
android:name=".GodotGame"

View file

@ -34,12 +34,9 @@ import android.Manifest
import android.app.ActivityManager
import android.app.ActivityOptions
import android.content.ComponentName
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.os.Debug
@ -74,9 +71,7 @@ import org.godotengine.godot.utils.DialogUtils
import org.godotengine.godot.utils.PermissionsUtil
import org.godotengine.godot.utils.ProcessPhoenix
import org.godotengine.openxr.vendors.utils.*
import java.io.File
import kotlin.math.min
import kotlin.text.indexOf
/**
* Base class for the Godot Android Editor activities.
@ -157,7 +152,6 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
internal const val GAME_MENU_ACTION_SET_TIME_SCALE = "setTimeScale"
private const val GAME_WORKSPACE = "Game"
private const val SCRIPT_WORKSPACE = "Script"
internal const val SNACKBAR_SHOW_DURATION_MS = 5000L
@ -204,12 +198,6 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
protected var gameMenuFragment: GameMenuFragment? = null
protected val gameMenuState = Bundle()
private val updatedCommandLineParams = ArrayList<String>()
private var changingOrientationAllowed = false
private var distractionFreeModeEnabled = false
private var activeWorkspace: String? = null
override fun getGodotAppLayout() = R.layout.godot_editor_layout
internal open fun getEditorWindowInfo() = EDITOR_MAIN_INFO
@ -256,7 +244,8 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
// Skip permissions request if running in a device farm (e.g. firebase test lab) or if requested via the launch
// intent (e.g. instrumentation tests).
val skipPermissionsRequest = isRunningInInstrumentation() || ActivityManager.isRunningInUserTestHarness()
val skipPermissionsRequest = isRunningInInstrumentation() ||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && ActivityManager.isRunningInUserTestHarness()
if (!skipPermissionsRequest) {
// We exclude certain permissions from the set we request at startup, as they'll be
// requested on demand based on use cases.
@ -265,7 +254,7 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
editorMessageDispatcher.parseStartIntent(packageManager, intent)
if (BuildConfig.BUILD_TYPE == "debug" && WAIT_FOR_DEBUGGER) {
if (BuildConfig.BUILD_TYPE == "dev" && WAIT_FOR_DEBUGGER) {
Debug.waitForDebugger()
}
@ -275,14 +264,6 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
setupGameMenuBar()
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
// Show EditorTitleBar on small screens only in landscape due to width limitations in portrait.
// TODO: Enable for portrait once the title bar width is optimized.
EditorUtils.toggleTitleBar(isLargeScreen || newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
}
override fun onDestroy() {
gradleBuildProvider.buildEnvDisconnect()
super.onDestroy()
@ -338,91 +319,6 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
super.onNewIntent(newIntent)
}
override fun handleStartIntent(intent: Intent, newLaunch: Boolean) {
when (intent.action) {
Intent.ACTION_VIEW -> {
val rootDir = Environment.getExternalStorageDirectory().canonicalPath
val dataPath = when (intent.scheme) {
ContentResolver.SCHEME_FILE -> {
intent.data?.path
}
ContentResolver.SCHEME_CONTENT -> {
// This approach is not recommend with 'content' scheme, but we require the filesystem path in
// order to open its parent directory and load the project.
val uriPath = intent.data?.path
if (uriPath != null) {
// Try and see if the external storage directory is part of the uri path.
val rootDirIndex = uriPath.indexOf(rootDir)
if (rootDirIndex != -1) {
uriPath.substring(rootDirIndex)
} else {
// Try and see if we can retrieve an existing relative path.
val pathParts = uriPath.split(':', '/')
var currentPath = ""
for (index in pathParts.size -1 downTo 0) {
currentPath = if (currentPath == "") {
pathParts[index]
} else {
"${pathParts[index]}/$currentPath"
}
val currentFile = File(rootDir, currentPath)
if (currentFile.exists()) {
break
}
}
currentPath
}
} else {
null
}
}
else -> null
}
if (!dataPath.isNullOrBlank()) {
var dataFile = File(dataPath)
if (!dataFile.isAbsolute) {
dataFile = File(rootDir, dataPath)
}
val dataDir = dataFile.parentFile
if (dataDir?.isDirectory == true) {
val loadProjectArgs = arrayOf(EDITOR_ARG, PATH_ARG, dataDir.absolutePath)
if (newLaunch) {
// Update the command line parameters to load the specified project.
updatedCommandLineParams.addAll(loadProjectArgs)
} else {
// Check if we are already editing the specified directory.
var isEditor = false
var nextIsPath = false
var currentPath = ""
for (arg in commandLine) {
if (nextIsPath) {
currentPath = arg
nextIsPath = false
}
if (arg == EDITOR_ARG || arg == EDITOR_ARG_SHORT) {
isEditor = true
} else if (arg == PATH_ARG) {
nextIsPath = true
}
}
if (!isEditor || currentPath != dataDir.absolutePath) {
onNewGodotInstanceRequested(loadProjectArgs)
}
}
}
}
}
}
super.handleStartIntent(intent, newLaunch)
}
protected open fun shouldShowGameMenuBar() = gameMenuContainer != null
private fun setupGameMenuBar() {
@ -449,7 +345,6 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
val longPressEnabled = enableLongPressGestures()
val panScaleEnabled = enablePanAndScaleGestures()
val overrideVolumeButtonsEnabled = overrideVolumeButtons()
val hapticEnabled = enableHapticOnLongPress()
runOnUiThread {
// Enable long press, panning and scaling gestures
@ -457,7 +352,6 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
enableLongPress(longPressEnabled)
enablePanningAndScalingGestures(panScaleEnabled)
setOverrideVolumeButtons(overrideVolumeButtonsEnabled)
enableHapticFeedback(hapticEnabled)
}
}
}
@ -510,10 +404,7 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
override fun getCommandLine(): MutableList<String> {
val params = super.getCommandLine()
if (updatedCommandLineParams.isNotEmpty()) {
params.addAll(updatedCommandLineParams)
}
if (BuildConfig.BUILD_TYPE == "debug" && !params.contains("--benchmark")) {
if (BuildConfig.BUILD_TYPE == "dev" && !params.contains("--benchmark")) {
params.add("--benchmark")
}
return params
@ -712,7 +603,7 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
/**
* The Godot Android Editor sets its own orientation via its AndroidManifest
*/
protected open fun overrideOrientationRequest() = !changingOrientationAllowed
protected open fun overrideOrientationRequest() = true
protected open fun overrideVolumeButtons() = false
@ -722,12 +613,6 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
protected open fun enableLongPressGestures() =
java.lang.Boolean.parseBoolean(GodotLib.getEditorSetting("interface/touchscreen/enable_long_press_as_right_click"))
/**
* Enable haptic feedback on long-press right-click for the Godot Android editor.
*/
protected open fun enableHapticOnLongPress() =
java.lang.Boolean.parseBoolean(GodotLib.getEditorSetting("interface/touchscreen/haptic_on_long_press"))
/**
* Disable scroll deadzone for the Godot Android editor.
*/
@ -804,7 +689,7 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
}
PermissionsUtil.REQUEST_INSTALL_PACKAGES_REQ_CODE -> {
if (!packageManager.canRequestPackageInstalls()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !packageManager.canRequestPackageInstalls()) {
Toast.makeText(
this,
R.string.denied_install_packages_permission_error_msg,
@ -916,8 +801,6 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
}
override fun onEditorWorkspaceSelected(workspace: String) {
activeWorkspace = workspace
if (workspace == GAME_WORKSPACE && shouldShowGameMenuBar()) {
if (editorMessageDispatcher.bringEditorWindowToFront(EMBEDDED_RUN_GAME_INFO) || editorMessageDispatcher.bringEditorWindowToFront(RUN_GAME_INFO)) {
return
@ -930,23 +813,6 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
embeddedGameViewContainerWindow?.isVisible = true
}
}
toggleScriptEditorOrientation()
}
override fun onDistractionFreeModeChanged(enabled: Boolean) {
distractionFreeModeEnabled = enabled
toggleScriptEditorOrientation()
}
private fun toggleScriptEditorOrientation() {
if (activeWorkspace == SCRIPT_WORKSPACE && distractionFreeModeEnabled) {
changingOrientationAllowed = true
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER
} else if (changingOrientationAllowed) {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
changingOrientationAllowed = false
}
}
internal open fun bringSelfToFront() {

View file

@ -30,7 +30,12 @@
package org.godotengine.editor
import android.app.PictureInPictureParams
import android.content.pm.PackageManager
import android.graphics.Rect
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.annotation.CallSuper
import androidx.core.view.isVisible
@ -50,6 +55,7 @@ open class GodotGame : BaseGodotGame() {
private val TAG = GodotGame::class.java.simpleName
}
private val gameViewSourceRectHint = Rect()
private val expandGameMenuButton: View? by lazy { findViewById(R.id.game_menu_expand_button) }
override fun onCreate(savedInstanceState: Bundle?) {
@ -69,6 +75,13 @@ open class GodotGame : BaseGodotGame() {
gameMenuFragment?.expandGameMenu()
}
}
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 getCommandLine(): MutableList<String> {
@ -83,7 +96,27 @@ open class GodotGame : BaseGodotGame() {
return updatedArgs
}
override fun isPiPEnabled() = true
override fun enterPiPMode() {
if (hasPiPSystemFeature()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val builder = PictureInPictureParams.Builder().setSourceRectHint(gameViewSourceRectHint)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
builder.setSeamlessResizeEnabled(false)
}
setPictureInPictureParams(builder.build())
}
Log.v(TAG, "Entering PiP mode")
enterPictureInPictureMode()
}
}
/**
* Returns true the if the device supports picture-in-picture (PiP).
*/
protected fun hasPiPSystemFeature(): Boolean {
return packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
}
override fun shouldShowGameMenuBar(): Boolean {
return intent.getBooleanExtra(
@ -94,11 +127,21 @@ open class GodotGame : BaseGodotGame() {
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode)
Log.v(TAG, "onPictureInPictureModeChanged: $isInPictureInPictureMode")
// Hide the game menu fragment when in PiP.
gameMenuContainer?.isVisible = !isInPictureInPictureMode
}
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 getGodotAppLayout() = R.layout.godot_game_layout
override fun getEditorWindowInfo() = RUN_GAME_INFO
@ -215,7 +258,7 @@ open class GodotGame : BaseGodotGame() {
override fun isCloseButtonEnabled() = !isHorizonOSDevice(applicationContext)
override fun isPiPButtonEnabled() = isPiPModeSupported()
override fun isPiPButtonEnabled() = hasPiPSystemFeature()
override fun isMenuBarCollapsable() = true

View file

@ -140,7 +140,7 @@ class EmbeddedGodotGame : GodotGame() {
override fun isMenuBarCollapsable() = false
override fun isAlwaysOnTopSupported() = isPiPModeSupported()
override fun isAlwaysOnTopSupported() = hasPiPSystemFeature()
override fun onFullScreenUpdated(enabled: Boolean) {
godot?.enableImmersiveMode(enabled)

View file

@ -47,7 +47,6 @@ import androidx.fragment.app.Fragment
import org.godotengine.editor.BaseGodotEditor
import org.godotengine.editor.BaseGodotEditor.Companion.SNACKBAR_SHOW_DURATION_MS
import org.godotengine.editor.R
import org.godotengine.godot.feature.PictureInPictureProvider
import org.godotengine.godot.utils.DialogUtils
/**
@ -66,7 +65,7 @@ class GameMenuFragment : Fragment(), PopupMenu.OnMenuItemClickListener {
/**
* Used to be notified of events fired when interacting with the game menu.
*/
interface GameMenuListener : PictureInPictureProvider {
interface GameMenuListener {
/**
* Kotlin representation of the RuntimeNodeSelect::SelectMode enum in 'scene/debugger/scene_debugger.h'.
@ -110,6 +109,7 @@ class GameMenuFragment : Fragment(), PopupMenu.OnMenuItemClickListener {
fun isGameEmbeddingSupported(): Boolean
fun embedGameOnPlay(embedded: Boolean)
fun enterPiPMode() {}
fun minimizeGameWindow() {}
fun closeGameWindow() {}
@ -181,7 +181,10 @@ class GameMenuFragment : Fragment(), PopupMenu.OnMenuItemClickListener {
PopupMenu(context, optionsButton).apply {
setOnMenuItemClickListener(this@GameMenuFragment)
inflate(R.menu.options_menu)
menu.setGroupDividerEnabled(true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
menu.setGroupDividerEnabled(true)
}
}
}

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()) {