summaryrefslogtreecommitdiffstats
path: root/platform/android/java/editor/src
diff options
context:
space:
mode:
authorFredia Huya-Kouadio <fhuya@meta.com>2024-04-20 10:24:11 -0700
committerFredia Huya-Kouadio <fhuya@meta.com>2024-09-06 00:35:50 -0700
commit9dc0543da7323e970f4349e65b416ef31056df20 (patch)
tree7350403dbba62a1a74830cbdb44978cdb7bda4ae /platform/android/java/editor/src
parent835808ed8fa992c961d6989f0a0c48ed2abd69bd (diff)
downloadredot-engine-9dc0543da7323e970f4349e65b416ef31056df20.tar.gz
Improve support for XR projects
Diffstat (limited to 'platform/android/java/editor/src')
-rw-r--r--platform/android/java/editor/src/google/java/org/godotengine/editor/GodotEditor.kt39
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt (renamed from platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt)100
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/EditorMessageDispatcher.kt10
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt57
-rw-r--r--platform/android/java/editor/src/meta/AndroidManifest.xml99
-rw-r--r--platform/android/java/editor/src/meta/assets/vr_splash.pngbin0 -> 14766 bytes
-rw-r--r--platform/android/java/editor/src/meta/java/org/godotengine/editor/GodotEditor.kt94
-rw-r--r--platform/android/java/editor/src/meta/java/org/godotengine/editor/GodotXRGame.kt71
8 files changed, 421 insertions, 49 deletions
diff --git a/platform/android/java/editor/src/google/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/google/java/org/godotengine/editor/GodotEditor.kt
new file mode 100644
index 0000000000..f15d9f7768
--- /dev/null
+++ b/platform/android/java/editor/src/google/java/org/godotengine/editor/GodotEditor.kt
@@ -0,0 +1,39 @@
+/**************************************************************************/
+/* GodotEditor.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.editor
+
+/**
+ * Primary window of the Godot Editor.
+ *
+ * This is the implementation of the editor used when running on regular Android devices.
+ */
+open class GodotEditor : BaseGodotEditor() {
+}
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt
index 405b2fb57f..50daf44d2a 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* GodotEditor.kt */
+/* BaseGodotEditor.kt */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -52,6 +52,8 @@ 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.isNativeXRDevice
import java.util.*
import kotlin.math.min
@@ -61,13 +63,11 @@ import kotlin.math.min
* This provides the basic templates for the activities making up this application.
* Each derived activity runs in its own process, which enable up to have several instances of
* the Godot engine up and running at the same time.
- *
- * It also plays the role of the primary editor window.
*/
-open class GodotEditor : GodotActivity() {
+abstract class BaseGodotEditor : GodotActivity() {
companion object {
- private val TAG = GodotEditor::class.java.simpleName
+ private val TAG = BaseGodotEditor::class.java.simpleName
private const val WAIT_FOR_DEBUGGER = false
@@ -81,12 +81,13 @@ open class GodotEditor : GodotActivity() {
// Command line arguments
private const val FULLSCREEN_ARG = "--fullscreen"
private const val FULLSCREEN_ARG_SHORT = "-f"
- private const val EDITOR_ARG = "--editor"
- private const val EDITOR_ARG_SHORT = "-e"
- private const val EDITOR_PROJECT_MANAGER_ARG = "--project-manager"
- private const val EDITOR_PROJECT_MANAGER_ARG_SHORT = "-p"
- private const val BREAKPOINTS_ARG = "--breakpoints"
- private const val BREAKPOINTS_ARG_SHORT = "-b"
+ internal const val EDITOR_ARG = "--editor"
+ internal const val EDITOR_ARG_SHORT = "-e"
+ internal const val EDITOR_PROJECT_MANAGER_ARG = "--project-manager"
+ internal const val EDITOR_PROJECT_MANAGER_ARG_SHORT = "-p"
+ internal const val BREAKPOINTS_ARG = "--breakpoints"
+ internal const val BREAKPOINTS_ARG_SHORT = "-b"
+ internal const val XR_MODE_ARG = "--xr-mode"
// Info for the various classes used by the editor
internal val EDITOR_MAIN_INFO = EditorWindowInfo(GodotEditor::class.java, 777, "")
@@ -122,6 +123,20 @@ open class GodotEditor : GodotActivity() {
internal open fun getEditorWindowInfo() = EDITOR_MAIN_INFO
+ /**
+ * Set of permissions to be excluded when requesting all permissions at startup.
+ *
+ * The permissions in this set will be requested on demand based on use cases.
+ */
+ @CallSuper
+ protected open fun getExcludedPermissions(): MutableSet<String> {
+ return mutableSetOf(
+ // The RECORD_AUDIO permission is requested when the "audio/driver/enable_input" project
+ // setting is enabled.
+ Manifest.permission.RECORD_AUDIO
+ )
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
@@ -131,8 +146,8 @@ open class GodotEditor : GodotActivity() {
}
// We exclude certain permissions from the set we request at startup, as they'll be
- // requested on demand based on use-cases.
- PermissionsUtil.requestManifestPermissions(this, setOf(Manifest.permission.RECORD_AUDIO))
+ // requested on demand based on use cases.
+ PermissionsUtil.requestManifestPermissions(this, getExcludedPermissions())
val params = intent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS)
Log.d(TAG, "Starting intent $intent with parameters ${params.contentToString()}")
@@ -152,8 +167,6 @@ open class GodotEditor : GodotActivity() {
val longPressEnabled = enableLongPressGestures()
val panScaleEnabled = enablePanAndScaleGestures()
- checkForProjectPermissionsToEnable()
-
runOnUiThread {
// Enable long press, panning and scaling gestures
godotFragment?.godot?.renderView?.inputHandler?.apply {
@@ -171,17 +184,6 @@ open class GodotEditor : GodotActivity() {
}
}
- /**
- * Check for project permissions to enable
- */
- protected open fun checkForProjectPermissionsToEnable() {
- // Check for RECORD_AUDIO permission
- val audioInputEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("audio/driver/enable_input"))
- if (audioInputEnabled) {
- PermissionsUtil.requestPermission(Manifest.permission.RECORD_AUDIO, this)
- }
- }
-
@CallSuper
protected open fun updateCommandLineParams(args: List<String>) {
// Update the list of command line params with the new args
@@ -196,7 +198,7 @@ open class GodotEditor : GodotActivity() {
final override fun getCommandLine() = commandLineParams
- protected open fun getEditorWindowInfo(args: Array<String>): EditorWindowInfo {
+ protected open fun retrieveEditorWindowInfo(args: Array<String>): EditorWindowInfo {
var hasEditor = false
var i = 0
@@ -273,7 +275,7 @@ open class GodotEditor : GodotActivity() {
}
override fun onNewGodotInstanceRequested(args: Array<String>): Int {
- val editorWindowInfo = getEditorWindowInfo(args)
+ val editorWindowInfo = retrieveEditorWindowInfo(args)
// Launch a new activity
val sourceView = godotFragment?.view
@@ -405,20 +407,26 @@ open class GodotEditor : GodotActivity() {
return when (policy) {
LaunchPolicy.AUTO -> {
- try {
- when (Integer.parseInt(GodotLib.getEditorSetting("run/window_placement/android_window"))) {
- ANDROID_WINDOW_SAME_AS_EDITOR -> LaunchPolicy.SAME
- ANDROID_WINDOW_SIDE_BY_SIDE_WITH_EDITOR -> LaunchPolicy.ADJACENT
- ANDROID_WINDOW_SAME_AS_EDITOR_AND_LAUNCH_IN_PIP_MODE -> LaunchPolicy.SAME_AND_LAUNCH_IN_PIP_MODE
- else -> {
- // ANDROID_WINDOW_AUTO
- defaultLaunchPolicy
+ if (isHorizonOSDevice()) {
+ // Horizon OS UX is more desktop-like and has support for launching adjacent
+ // windows. So we always want to launch in adjacent mode when auto is selected.
+ LaunchPolicy.ADJACENT
+ } else {
+ try {
+ when (Integer.parseInt(GodotLib.getEditorSetting("run/window_placement/android_window"))) {
+ ANDROID_WINDOW_SAME_AS_EDITOR -> LaunchPolicy.SAME
+ ANDROID_WINDOW_SIDE_BY_SIDE_WITH_EDITOR -> LaunchPolicy.ADJACENT
+ ANDROID_WINDOW_SAME_AS_EDITOR_AND_LAUNCH_IN_PIP_MODE -> LaunchPolicy.SAME_AND_LAUNCH_IN_PIP_MODE
+ else -> {
+ // ANDROID_WINDOW_AUTO
+ defaultLaunchPolicy
+ }
}
+ } catch (e: NumberFormatException) {
+ Log.w(TAG, "Error parsing the Android window placement editor setting", e)
+ // Fall-back to the default launch policy
+ defaultLaunchPolicy
}
- } catch (e: NumberFormatException) {
- Log.w(TAG, "Error parsing the Android window placement editor setting", e)
- // Fall-back to the default launch policy
- defaultLaunchPolicy
}
}
@@ -431,8 +439,16 @@ open class GodotEditor : GodotActivity() {
/**
* Returns true the if the device supports picture-in-picture (PiP)
*/
- protected open fun hasPiPSystemFeature() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
- packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
+ 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)
+ }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/EditorMessageDispatcher.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/EditorMessageDispatcher.kt
index b16e62149a..f5a6ed7dab 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/EditorMessageDispatcher.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/EditorMessageDispatcher.kt
@@ -42,9 +42,9 @@ import android.util.Log
import java.util.concurrent.ConcurrentHashMap
/**
- * Used by the [GodotEditor] classes to dispatch messages across processes.
+ * Used by the [BaseGodotEditor] classes to dispatch messages across processes.
*/
-internal class EditorMessageDispatcher(private val editor: GodotEditor) {
+internal class EditorMessageDispatcher(private val editor: BaseGodotEditor) {
companion object {
private val TAG = EditorMessageDispatcher::class.java.simpleName
@@ -173,7 +173,11 @@ internal class EditorMessageDispatcher(private val editor: GodotEditor) {
// to the sender.
val senderId = messengerBundle.getInt(KEY_EDITOR_ID)
val senderMessenger: Messenger? = messengerBundle.getParcelable(KEY_EDITOR_MESSENGER)
- registerMessenger(senderId, senderMessenger)
+ registerMessenger(senderId, senderMessenger) {
+ // Terminate current instance when parent is no longer available.
+ Log.d(TAG, "Terminating current editor instance because parent is no longer available")
+ editor.finish()
+ }
// Register ourselves to the sender so that it can communicate with us.
registerSelfTo(pm, senderMessenger, editor.getEditorWindowInfo().windowId)
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
index 6b4bf255f2..e52d566347 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
@@ -30,6 +30,7 @@
package org.godotengine.editor
+import android.Manifest
import android.annotation.SuppressLint
import android.app.PictureInPictureParams
import android.content.Intent
@@ -38,12 +39,15 @@ import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.View
+import androidx.annotation.CallSuper
import org.godotengine.godot.GodotLib
+import org.godotengine.godot.utils.PermissionsUtil
+import org.godotengine.godot.utils.ProcessPhoenix
/**
* Drives the 'run project' window of the Godot Editor.
*/
-class GodotGame : GodotEditor() {
+open class GodotGame : GodotEditor() {
companion object {
private val TAG = GodotGame::class.java.simpleName
@@ -136,8 +140,53 @@ class GodotGame : GodotEditor() {
override fun enablePanAndScaleGestures() = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/pointing/android/enable_pan_and_scale_gestures"))
- override fun checkForProjectPermissionsToEnable() {
- // Nothing to do.. by the time we get here, the project permissions will have already
- // been requested by the Editor window.
+ override fun onGodotSetupCompleted() {
+ super.onGodotSetupCompleted()
+ Log.v(TAG, "OnGodotSetupCompleted")
+
+ // Check if we should be running in XR instead (if available) as it's possible we were
+ // launched from the project manager which doesn't have that information.
+ val launchingArgs = intent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS)
+ if (launchingArgs != null) {
+ val editorWindowInfo = retrieveEditorWindowInfo(launchingArgs)
+ if (editorWindowInfo != getEditorWindowInfo()) {
+ val relaunchIntent = getNewGodotInstanceIntent(editorWindowInfo, launchingArgs)
+ relaunchIntent.putExtra(EXTRA_NEW_LAUNCH, true)
+ .putExtra(EditorMessageDispatcher.EXTRA_MSG_DISPATCHER_PAYLOAD, intent.getBundleExtra(EditorMessageDispatcher.EXTRA_MSG_DISPATCHER_PAYLOAD))
+
+ Log.d(TAG, "Relaunching XR project using ${editorWindowInfo.windowClassName} with parameters ${launchingArgs.contentToString()}")
+ val godot = godot
+ if (godot != null) {
+ godot.destroyAndKillProcess {
+ ProcessPhoenix.triggerRebirth(this, relaunchIntent)
+ }
+ } else {
+ ProcessPhoenix.triggerRebirth(this, relaunchIntent)
+ }
+ return
+ }
+ }
+
+ // Request project runtime permissions if necessary
+ val permissionsToEnable = getProjectPermissionsToEnable()
+ if (permissionsToEnable.isNotEmpty()) {
+ PermissionsUtil.requestPermissions(this, permissionsToEnable)
+ }
+ }
+
+ /**
+ * Check for project permissions to enable
+ */
+ @CallSuper
+ protected open fun getProjectPermissionsToEnable(): MutableList<String> {
+ val permissionsToEnable = mutableListOf<String>()
+
+ // Check for RECORD_AUDIO permission
+ val audioInputEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("audio/driver/enable_input"))
+ if (audioInputEnabled) {
+ permissionsToEnable.add(Manifest.permission.RECORD_AUDIO)
+ }
+
+ return permissionsToEnable
}
}
diff --git a/platform/android/java/editor/src/meta/AndroidManifest.xml b/platform/android/java/editor/src/meta/AndroidManifest.xml
new file mode 100644
index 0000000000..06442ac4e6
--- /dev/null
+++ b/platform/android/java/editor/src/meta/AndroidManifest.xml
@@ -0,0 +1,99 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:horizonos="http://schemas.horizonos/sdk">
+
+ <horizonos:uses-horizonos-sdk
+ horizonos:minSdkVersion="69"
+ horizonos:targetSdkVersion="69" />
+
+ <uses-feature
+ android:name="android.hardware.vr.headtracking"
+ android:required="true"
+ android:version="1"/>
+
+ <!-- Oculus Quest hand tracking -->
+ <uses-permission android:name="com.oculus.permission.HAND_TRACKING" />
+ <uses-feature
+ android:name="oculus.software.handtracking"
+ android:required="false" />
+
+ <!-- Passthrough feature flag -->
+ <uses-feature android:name="com.oculus.feature.PASSTHROUGH"
+ android:required="false" />
+
+ <!-- Overlay keyboard support -->
+ <uses-feature android:name="oculus.software.overlay_keyboard" android:required="false"/>
+
+ <!-- Render model -->
+ <uses-permission android:name="com.oculus.permission.RENDER_MODEL" />
+ <uses-feature android:name="com.oculus.feature.RENDER_MODEL" android:required="false" />
+
+ <!-- Anchor api -->
+ <uses-permission android:name="com.oculus.permission.USE_ANCHOR_API" />
+
+ <!-- Scene api -->
+ <uses-permission android:name="com.oculus.permission.USE_SCENE" />
+
+ <application>
+
+ <activity
+ android:name=".GodotEditor"
+ android:exported="true"
+ android:screenOrientation="landscape"
+ tools:node="merge"
+ tools:replace="android:screenOrientation">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ <category android:name="com.oculus.intent.category.2D" />
+ </intent-filter>
+
+ <meta-data android:name="com.oculus.vrshell.free_resizing_lock_aspect_ratio" android:value="true"/>
+ </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">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="com.oculus.intent.category.VR" />
+ <category android:name="org.khronos.openxr.intent.category.IMMERSIVE_HMD" />
+ </intent-filter>
+ </activity>
+
+ <!-- Supported Meta devices -->
+ <meta-data
+ android:name="com.oculus.supportedDevices"
+ android:value="quest3|questpro"
+ tools:replace="android:value" />
+
+ <!--
+ We remove this meta-data originating from the vendors plugin as we only need the loader for
+ now since the project being edited provides its own version of the vendors plugin.
+
+ This needs to be removed once we start implementing the immersive version of the project
+ manager and editor windows.
+ -->
+ <meta-data
+ android:name="org.godotengine.plugin.v2.GodotOpenXRMeta"
+ android:value="org.godotengine.openxr.vendors.meta.GodotOpenXRMeta"
+ tools:node="remove" />
+
+ <!-- Enable system splash screen -->
+ <meta-data android:name="com.oculus.ossplash" android:value="true"/>
+ <!-- Enable passthrough background during the splash screen -->
+ <meta-data android:name="com.oculus.ossplash.background" android:value="passthrough-contextual"/>
+
+ </application>
+
+</manifest>
diff --git a/platform/android/java/editor/src/meta/assets/vr_splash.png b/platform/android/java/editor/src/meta/assets/vr_splash.png
new file mode 100644
index 0000000000..7bddd4325a
--- /dev/null
+++ b/platform/android/java/editor/src/meta/assets/vr_splash.png
Binary files differ
diff --git a/platform/android/java/editor/src/meta/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/meta/java/org/godotengine/editor/GodotEditor.kt
new file mode 100644
index 0000000000..9f0440e87d
--- /dev/null
+++ b/platform/android/java/editor/src/meta/java/org/godotengine/editor/GodotEditor.kt
@@ -0,0 +1,94 @@
+/**************************************************************************/
+/* GodotEditor.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.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 Meta 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")
+
+ internal const val USE_ANCHOR_API_PERMISSION = "com.oculus.permission.USE_ANCHOR_API"
+ internal const val USE_SCENE_PERMISSION = "com.oculus.permission.USE_SCENE"
+ }
+
+ override fun getExcludedPermissions(): MutableSet<String> {
+ val excludedPermissions = super.getExcludedPermissions()
+ // The USE_ANCHOR_API and USE_SCENE permissions are requested when the "xr/openxr/enabled"
+ // project setting is enabled.
+ excludedPermissions.add(USE_ANCHOR_API_PERMISSION)
+ excludedPermissions.add(USE_SCENE_PERMISSION)
+ return excludedPermissions
+ }
+
+ 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)
+ }
+ }
+}
diff --git a/platform/android/java/editor/src/meta/java/org/godotengine/editor/GodotXRGame.kt b/platform/android/java/editor/src/meta/java/org/godotengine/editor/GodotXRGame.kt
new file mode 100644
index 0000000000..d71fbb53f2
--- /dev/null
+++ b/platform/android/java/editor/src/meta/java/org/godotengine/editor/GodotXRGame.kt
@@ -0,0 +1,71 @@
+/*************************************************************************/
+/* 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.utils.PermissionsUtil
+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
+
+ override fun getProjectPermissionsToEnable(): MutableList<String> {
+ val permissionsToEnable = super.getProjectPermissionsToEnable()
+
+ val openxrEnabled = GodotLib.getGlobal("xr/openxr/enabled").toBoolean()
+ if (openxrEnabled) {
+ permissionsToEnable.add(USE_ANCHOR_API_PERMISSION)
+ permissionsToEnable.add(USE_SCENE_PERMISSION)
+ }
+
+ return permissionsToEnable
+ }
+}