From b4f25b186369768583979d6a5d5faff9ed1fbdae Mon Sep 17 00:00:00 2001 From: Fredia Huya-Kouadio Date: Thu, 16 Jan 2025 09:40:30 -0800 Subject: [PATCH] Clean up the XR editor logic - Coalesce common logic into the `main` flavor to avoid duplication - Code cleanup --- platform/android/export/export_plugin.cpp | 7 ++ .../org/godotengine/editor/GodotEditor.kt | 3 +- .../editor/src/horizonos/AndroidManifest.xml | 9 +-- .../org/godotengine/editor/GodotEditor.kt | 69 ++----------------- .../java/editor/src/main/AndroidManifest.xml | 12 ++++ .../org/godotengine/editor/BaseGodotEditor.kt | 65 ++++++++++++----- .../org/godotengine/editor/GodotXRGame.kt | 6 +- .../editor/src/main/res/values/dimens.xml | 2 +- .../editor/src/picoos/AndroidManifest.xml | 9 +-- .../org/godotengine/editor/GodotEditor.kt | 46 +------------ .../org/godotengine/editor/GodotXRGame.kt | 59 ---------------- .../godotengine/godot/GodotRenderView.java | 4 +- .../godotengine/godot/utils/DeviceUtils.kt | 9 +-- 13 files changed, 86 insertions(+), 214 deletions(-) rename platform/android/java/editor/src/{horizonos => main}/java/org/godotengine/editor/GodotXRGame.kt (94%) delete mode 100644 platform/android/java/editor/src/picoos/java/org/godotengine/editor/GodotXRGame.kt diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 232d7cf278f..c6991d777ea 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -2840,6 +2840,13 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Refget("screen/immersive_mode"); diff --git a/platform/android/java/editor/src/android/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/android/java/org/godotengine/editor/GodotEditor.kt index f15d9f77682..4b0d4ecb053 100644 --- a/platform/android/java/editor/src/android/java/org/godotengine/editor/GodotEditor.kt +++ b/platform/android/java/editor/src/android/java/org/godotengine/editor/GodotEditor.kt @@ -35,5 +35,4 @@ package org.godotengine.editor * * This is the implementation of the editor used when running on regular Android devices. */ -open class GodotEditor : BaseGodotEditor() { -} +open class GodotEditor : BaseGodotEditor() diff --git a/platform/android/java/editor/src/horizonos/AndroidManifest.xml b/platform/android/java/editor/src/horizonos/AndroidManifest.xml index 68ba11fd843..3f2b87e0485 100644 --- a/platform/android/java/editor/src/horizonos/AndroidManifest.xml +++ b/platform/android/java/editor/src/horizonos/AndroidManifest.xml @@ -57,15 +57,8 @@ + tools:node="merge"> diff --git a/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotEditor.kt index 0064f1e81c8..ba8947bcd90 100644 --- a/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotEditor.kt +++ b/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotEditor.kt @@ -30,9 +30,6 @@ package org.godotengine.editor -import org.godotengine.godot.GodotLib -import org.godotengine.godot.utils.isNativeXRDevice - /** * Primary window of the Godot Editor. * @@ -40,66 +37,10 @@ import org.godotengine.godot.utils.isNativeXRDevice */ open class GodotEditor : BaseGodotEditor() { - companion object { - private val TAG = GodotEditor::class.java.simpleName - - /** Default behavior, means we check project settings **/ - private const val XR_MODE_DEFAULT = "default" - - /** - * Ignore project settings, OpenXR is disabled - */ - private const val XR_MODE_OFF = "off" - - /** - * Ignore project settings, OpenXR is enabled - */ - private const val XR_MODE_ON = "on" - - internal val XR_RUN_GAME_INFO = EditorWindowInfo(GodotXRGame::class.java, 1667, ":GodotXRGame") - - internal val USE_SCENE_PERMISSIONS = listOf("com.oculus.permission.USE_SCENE", "horizonos.permission.USE_SCENE") - } - - override fun getExcludedPermissions(): MutableSet { - val excludedPermissions = super.getExcludedPermissions() - // The USE_SCENE permission is requested when the "xr/openxr/enabled" project setting - // is enabled. - excludedPermissions.addAll(USE_SCENE_PERMISSIONS) - return excludedPermissions - } - - override fun retrieveEditorWindowInfo(args: Array): EditorWindowInfo { - var hasEditor = false - var xrMode = XR_MODE_DEFAULT - - var i = 0 - while (i < args.size) { - when (args[i++]) { - EDITOR_ARG, EDITOR_ARG_SHORT, EDITOR_PROJECT_MANAGER_ARG, EDITOR_PROJECT_MANAGER_ARG_SHORT -> hasEditor = true - XR_MODE_ARG -> { - xrMode = args[i++] - } - } - } - - return if (hasEditor) { - EDITOR_MAIN_INFO - } else { - val openxrEnabled = xrMode == XR_MODE_ON || - (xrMode == XR_MODE_DEFAULT && GodotLib.getGlobal("xr/openxr/enabled").toBoolean()) - if (openxrEnabled && isNativeXRDevice()) { - XR_RUN_GAME_INFO - } else { - RUN_GAME_INFO - } - } - } - - override fun getEditorWindowInfoForInstanceId(instanceId: Int): EditorWindowInfo? { - return when (instanceId) { - XR_RUN_GAME_INFO.windowId -> XR_RUN_GAME_INFO - else -> super.getEditorWindowInfoForInstanceId(instanceId) - } + override fun getXRRuntimePermissions(): MutableSet { + val xrRuntimePermissions = super.getXRRuntimePermissions() + xrRuntimePermissions.add("com.oculus.permission.USE_SCENE") + xrRuntimePermissions.add("horizonos.permission.USE_SCENE") + return xrRuntimePermissions } } diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml index f67c34e4485..78ccc9ca96f 100644 --- a/platform/android/java/editor/src/main/AndroidManifest.xml +++ b/platform/android/java/editor/src/main/AndroidManifest.xml @@ -71,6 +71,18 @@ android:defaultWidth="@dimen/editor_default_window_width" android:defaultHeight="@dimen/editor_default_window_height" /> + + diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt index d1f0c427983..b1fd8a1124d 100644 --- a/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt +++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt @@ -47,13 +47,12 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.window.layout.WindowMetricsCalculator import org.godotengine.editor.utils.signApk import org.godotengine.editor.utils.verifyApk +import org.godotengine.godot.BuildConfig import org.godotengine.godot.GodotActivity import org.godotengine.godot.GodotLib import org.godotengine.godot.error.Error import org.godotengine.godot.utils.PermissionsUtil import org.godotengine.godot.utils.ProcessPhoenix -import org.godotengine.godot.utils.isHorizonOSDevice -import org.godotengine.godot.utils.isPicoOSDevice import org.godotengine.godot.utils.isNativeXRDevice import java.util.* import kotlin.math.min @@ -93,6 +92,20 @@ abstract class BaseGodotEditor : GodotActivity() { // Info for the various classes used by the editor internal val EDITOR_MAIN_INFO = EditorWindowInfo(GodotEditor::class.java, 777, "") internal val RUN_GAME_INFO = EditorWindowInfo(GodotGame::class.java, 667, ":GodotGame", LaunchPolicy.AUTO, true) + internal val XR_RUN_GAME_INFO = EditorWindowInfo(GodotXRGame::class.java, 1667, ":GodotXRGame") + + /** Default behavior, means we check project settings **/ + private const val XR_MODE_DEFAULT = "default" + + /** + * Ignore project settings, OpenXR is disabled + */ + private const val XR_MODE_OFF = "off" + + /** + * Ignore project settings, OpenXR is enabled + */ + private const val XR_MODE_ON = "on" /** * Sets of constants to specify the window to use to run the project. @@ -129,8 +142,7 @@ abstract class BaseGodotEditor : GodotActivity() { * * The permissions in this set will be requested on demand based on use cases. */ - @CallSuper - protected open fun getExcludedPermissions(): MutableSet { + private fun getExcludedPermissions(): MutableSet { val excludedPermissions = mutableSetOf( // The RECORD_AUDIO permission is requested when the "audio/driver/enable_input" project // setting is enabled. @@ -144,9 +156,21 @@ abstract class BaseGodotEditor : GodotActivity() { Manifest.permission.REQUEST_INSTALL_PACKAGES, ) } + + // XR runtime permissions should only be requested when the "xr/openxr/enabled" project setting + // is enabled. + excludedPermissions.addAll(getXRRuntimePermissions()) return excludedPermissions } + /** + * Set of permissions to request when the "xr/openxr/enabled" project setting is enabled. + */ + @CallSuper + protected open fun getXRRuntimePermissions(): MutableSet { + return mutableSetOf() + } + override fun onCreate(savedInstanceState: Bundle?) { installSplashScreen() @@ -208,27 +232,38 @@ abstract class BaseGodotEditor : GodotActivity() { final override fun getCommandLine() = commandLineParams - protected open fun retrieveEditorWindowInfo(args: Array): EditorWindowInfo { + protected fun retrieveEditorWindowInfo(args: Array): EditorWindowInfo { var hasEditor = false + var xrMode = XR_MODE_DEFAULT var i = 0 while (i < args.size) { when (args[i++]) { EDITOR_ARG, EDITOR_ARG_SHORT, EDITOR_PROJECT_MANAGER_ARG, EDITOR_PROJECT_MANAGER_ARG_SHORT -> hasEditor = true + XR_MODE_ARG -> { + xrMode = args[i++] + } } } return if (hasEditor) { EDITOR_MAIN_INFO } else { - RUN_GAME_INFO + val openxrEnabled = xrMode == XR_MODE_ON || + (xrMode == XR_MODE_DEFAULT && GodotLib.getGlobal("xr/openxr/enabled").toBoolean()) + if (openxrEnabled && isNativeXRDevice(applicationContext)) { + XR_RUN_GAME_INFO + } else { + RUN_GAME_INFO + } } } - protected open fun getEditorWindowInfoForInstanceId(instanceId: Int): EditorWindowInfo? { + private fun getEditorWindowInfoForInstanceId(instanceId: Int): EditorWindowInfo? { return when (instanceId) { RUN_GAME_INFO.windowId -> RUN_GAME_INFO EDITOR_MAIN_INFO.windowId -> EDITOR_MAIN_INFO + XR_RUN_GAME_INFO.windowId -> XR_RUN_GAME_INFO else -> null } } @@ -417,8 +452,8 @@ abstract class BaseGodotEditor : GodotActivity() { return when (policy) { LaunchPolicy.AUTO -> { - if (isHorizonOSDevice()) { - // Horizon OS UX is more desktop-like and has support for launching adjacent + if (isNativeXRDevice(applicationContext)) { + // Native XR devices are more desktop-like and have support for launching adjacent // windows. So we always want to launch in adjacent mode when auto is selected. LaunchPolicy.ADJACENT } else { @@ -450,12 +485,6 @@ abstract class BaseGodotEditor : GodotActivity() { * Returns true the if the device supports picture-in-picture (PiP) */ protected open fun hasPiPSystemFeature(): Boolean { - if (isNativeXRDevice()) { - // Known native XR devices do not support PiP. - // Will need to revisit as they update their OS. - return false - } - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) } @@ -534,15 +563,15 @@ abstract class BaseGodotEditor : GodotActivity() { override fun supportsFeature(featureTag: String): Boolean { if (featureTag == "xr_editor") { - return isNativeXRDevice() + return isNativeXRDevice(applicationContext) } if (featureTag == "horizonos") { - return isHorizonOSDevice() + return BuildConfig.FLAVOR == "horizonos" } if (featureTag == "picoos") { - return isPicoOSDevice() + return BuildConfig.FLAVOR == "picoos" } return false diff --git a/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotXRGame.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotXRGame.kt similarity index 94% rename from platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotXRGame.kt rename to platform/android/java/editor/src/main/java/org/godotengine/editor/GodotXRGame.kt index 0c82791e891..bfaef3abbd1 100644 --- a/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotXRGame.kt +++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotXRGame.kt @@ -59,8 +59,8 @@ open class GodotXRGame: GodotGame() { override fun getProjectPermissionsToEnable(): MutableList { val permissionsToEnable = super.getProjectPermissionsToEnable() - val openxrEnabled = GodotLib.getGlobal("xr/openxr/enabled").toBoolean() - if (openxrEnabled) { + val xrRuntimePermission = getXRRuntimePermissions() + if (xrRuntimePermission.isNotEmpty() && GodotLib.getGlobal("xr/openxr/enabled").toBoolean()) { // We only request permissions when the `automatically_request_runtime_permissions` // project setting is enabled. // If the project setting is not defined, we fall-back to the default behavior which is @@ -69,7 +69,7 @@ open class GodotXRGame: GodotGame() { val automaticPermissionsRequestEnabled = automaticallyRequestPermissionsSetting.isNullOrEmpty() || automaticallyRequestPermissionsSetting.toBoolean() if (automaticPermissionsRequestEnabled) { - permissionsToEnable.addAll(USE_SCENE_PERMISSIONS) + permissionsToEnable.addAll(xrRuntimePermission) } } diff --git a/platform/android/java/editor/src/main/res/values/dimens.xml b/platform/android/java/editor/src/main/res/values/dimens.xml index 1e486872e6f..7e999dcce3d 100644 --- a/platform/android/java/editor/src/main/res/values/dimens.xml +++ b/platform/android/java/editor/src/main/res/values/dimens.xml @@ -1,5 +1,5 @@ - 640dp + 720dp 1024dp diff --git a/platform/android/java/editor/src/picoos/AndroidManifest.xml b/platform/android/java/editor/src/picoos/AndroidManifest.xml index 13ab40f90fc..53a13c09d2d 100644 --- a/platform/android/java/editor/src/picoos/AndroidManifest.xml +++ b/platform/android/java/editor/src/picoos/AndroidManifest.xml @@ -29,15 +29,8 @@ + tools:node="merge"> diff --git a/platform/android/java/editor/src/picoos/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/picoos/java/org/godotengine/editor/GodotEditor.kt index 2aee8b0f56a..d73dfeb08f9 100644 --- a/platform/android/java/editor/src/picoos/java/org/godotengine/editor/GodotEditor.kt +++ b/platform/android/java/editor/src/picoos/java/org/godotengine/editor/GodotEditor.kt @@ -30,53 +30,9 @@ package org.godotengine.editor -import org.godotengine.godot.GodotLib -import org.godotengine.godot.utils.isNativeXRDevice - /** * Primary window of the Godot Editor. * * This is the implementation of the editor used when running on PicoOS devices. */ -open class GodotEditor : BaseGodotEditor() { - - companion object { - private val TAG = GodotEditor::class.java.simpleName - - internal val XR_RUN_GAME_INFO = EditorWindowInfo(GodotXRGame::class.java, 1667, ":GodotXRGame") - } - - override fun retrieveEditorWindowInfo(args: Array): EditorWindowInfo { - var hasEditor = false - var xrModeOn = false - - var i = 0 - while (i < args.size) { - when (args[i++]) { - EDITOR_ARG, EDITOR_ARG_SHORT, EDITOR_PROJECT_MANAGER_ARG, EDITOR_PROJECT_MANAGER_ARG_SHORT -> hasEditor = true - XR_MODE_ARG -> { - val argValue = args[i++] - xrModeOn = xrModeOn || ("on" == argValue) - } - } - } - - return if (hasEditor) { - EDITOR_MAIN_INFO - } else { - val openxrEnabled = GodotLib.getGlobal("xr/openxr/enabled").toBoolean() - if (openxrEnabled && isNativeXRDevice()) { - XR_RUN_GAME_INFO - } else { - RUN_GAME_INFO - } - } - } - - override fun getEditorWindowInfoForInstanceId(instanceId: Int): EditorWindowInfo? { - return when (instanceId) { - XR_RUN_GAME_INFO.windowId -> XR_RUN_GAME_INFO - else -> super.getEditorWindowInfoForInstanceId(instanceId) - } - } -} +open class GodotEditor : BaseGodotEditor() diff --git a/platform/android/java/editor/src/picoos/java/org/godotengine/editor/GodotXRGame.kt b/platform/android/java/editor/src/picoos/java/org/godotengine/editor/GodotXRGame.kt deleted file mode 100644 index 2107a82d74c..00000000000 --- a/platform/android/java/editor/src/picoos/java/org/godotengine/editor/GodotXRGame.kt +++ /dev/null @@ -1,59 +0,0 @@ -/*************************************************************************/ -/* GodotXRGame.kt */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -package org.godotengine.editor - -import org.godotengine.godot.GodotLib -import org.godotengine.godot.xr.XRMode - -/** - * Provide support for running XR apps / games from the editor window. - */ -open class GodotXRGame: GodotGame() { - - override fun overrideOrientationRequest() = true - - override fun updateCommandLineParams(args: List) { - val updatedArgs = ArrayList() - if (!args.contains(XRMode.OPENXR.cmdLineArg)) { - updatedArgs.add(XRMode.OPENXR.cmdLineArg) - } - if (!args.contains(XR_MODE_ARG)) { - updatedArgs.add(XR_MODE_ARG) - updatedArgs.add("on") - } - updatedArgs.addAll(args) - - super.updateCommandLineParams(updatedArgs) - } - - override fun getEditorWindowInfo() = XR_RUN_GAME_INFO - -} 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 9db9ef6080e..9fe4fb20aa7 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java @@ -68,7 +68,7 @@ public interface GodotRenderView { * @return true if pointer capture is supported. */ default boolean canCapturePointer() { - // Pointer capture is not supported on Horizon OS - return !DeviceUtils.isHorizonOSDevice() && getInputHandler().canCapturePointer(); + // Pointer capture is not supported on native XR devices. + return !DeviceUtils.isNativeXRDevice(getView().getContext()) && getInputHandler().canCapturePointer(); } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/DeviceUtils.kt b/platform/android/java/lib/src/org/godotengine/godot/utils/DeviceUtils.kt index 7b99cce2162..3dbada05e3c 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/utils/DeviceUtils.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/utils/DeviceUtils.kt @@ -35,13 +35,14 @@ package org.godotengine.godot.utils +import android.content.Context import android.os.Build /** * Returns true if running on Meta Horizon OS. */ -fun isHorizonOSDevice(): Boolean { - return "Oculus".equals(Build.BRAND, true) +fun isHorizonOSDevice(context: Context): Boolean { + return context.packageManager.hasSystemFeature("oculus.hardware.standalone_vr") } /** @@ -54,6 +55,6 @@ fun isPicoOSDevice(): Boolean { /** * Returns true if running on a native Android XR device. */ -fun isNativeXRDevice(): Boolean { - return isHorizonOSDevice() || isPicoOSDevice() +fun isNativeXRDevice(context: Context): Boolean { + return isHorizonOSDevice(context) || isPicoOSDevice() }