Merge pull request #101645 from m4gr3d/disable_xr_mode_for_regular_launch
Clean up the XR editor logic
This commit is contained in:
@ -2840,6 +2840,13 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP
|
||||
command_line_strings.push_back("--xr_mode_openxr");
|
||||
} else { // XRMode.REGULAR is the default.
|
||||
command_line_strings.push_back("--xr_mode_regular");
|
||||
|
||||
// Also override the 'xr/openxr/enabled' project setting.
|
||||
// This is useful for multi-platforms projects supporting both XR and non-XR devices. The project would need
|
||||
// to enable openxr for development, and would create multiple XR and non-XR export presets.
|
||||
// These command line args ensure that the non-XR export presets will have openxr disabled.
|
||||
command_line_strings.push_back("--xr-mode");
|
||||
command_line_strings.push_back("off");
|
||||
}
|
||||
|
||||
bool immersive = p_preset->get("screen/immersive_mode");
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -57,15 +57,8 @@
|
||||
|
||||
<activity
|
||||
android:name=".GodotXRGame"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:process=":GodotXRGame"
|
||||
android:launchMode="singleTask"
|
||||
android:icon="@mipmap/ic_play_window"
|
||||
android:label="@string/godot_game_activity_name"
|
||||
android:exported="false"
|
||||
android:screenOrientation="landscape"
|
||||
android:resizeableActivity="false"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
|
||||
tools:node="merge">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
@ -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<String> {
|
||||
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<String>): 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<String> {
|
||||
val xrRuntimePermissions = super.getXRRuntimePermissions()
|
||||
xrRuntimePermissions.add("com.oculus.permission.USE_SCENE")
|
||||
xrRuntimePermissions.add("horizonos.permission.USE_SCENE")
|
||||
return xrRuntimePermissions
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,6 +71,18 @@
|
||||
android:defaultWidth="@dimen/editor_default_window_width"
|
||||
android:defaultHeight="@dimen/editor_default_window_height" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".GodotXRGame"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:process=":GodotXRGame"
|
||||
android:launchMode="singleTask"
|
||||
android:icon="@mipmap/ic_play_window"
|
||||
android:label="@string/godot_game_activity_name"
|
||||
android:exported="false"
|
||||
android:screenOrientation="landscape"
|
||||
android:resizeableActivity="false"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@ -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<String> {
|
||||
private fun getExcludedPermissions(): MutableSet<String> {
|
||||
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<String> {
|
||||
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<String>): EditorWindowInfo {
|
||||
protected fun retrieveEditorWindowInfo(args: Array<String>): 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
|
||||
|
||||
@ -59,8 +59,8 @@ open class GodotXRGame: GodotGame() {
|
||||
override fun getProjectPermissionsToEnable(): MutableList<String> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="editor_default_window_height">640dp</dimen>
|
||||
<dimen name="editor_default_window_height">720dp</dimen>
|
||||
<dimen name="editor_default_window_width">1024dp</dimen>
|
||||
</resources>
|
||||
|
||||
@ -29,15 +29,8 @@
|
||||
|
||||
<activity
|
||||
android:name=".GodotXRGame"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
|
||||
android:process=":GodotXRGame"
|
||||
android:launchMode="singleTask"
|
||||
android:icon="@mipmap/ic_play_window"
|
||||
android:label="@string/godot_game_activity_name"
|
||||
android:exported="false"
|
||||
android:screenOrientation="landscape"
|
||||
android:resizeableActivity="false"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
|
||||
tools:node="merge">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
@ -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<String>): 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()
|
||||
|
||||
@ -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<String>) {
|
||||
val updatedArgs = ArrayList<String>()
|
||||
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
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user