Make use of activity-alias as the launcher mechanism for the Godot editor and the Godot app template

This commit is contained in:
Fredia Huya-Kouadio
2025-11-06 11:30:07 -08:00
parent e47fb8b898
commit 2ed51e00a1
14 changed files with 391 additions and 81 deletions

View File

@@ -46,7 +46,7 @@
<activity
android:name=".GodotEditor"
android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
android:exported="true"
android:exported="false"
android:icon="@mipmap/themed_icon"
android:launchMode="singleTask"
android:screenOrientation="userLandscape">
@@ -54,13 +54,6 @@
android:defaultWidth="@dimen/editor_default_window_width"
android:defaultHeight="@dimen/editor_default_window_height" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- Intent filter used to intercept hybrid PANEL launch for the current editor project, and route it
properly through the editor 'run' logic (e.g: debugger setup) -->
<intent-filter>
@@ -77,6 +70,18 @@
<category android:name="org.godotengine.xr.hybrid.IMMERSIVE" />
</intent-filter>
</activity>
<activity-alias
android:name=".ProjectManager"
android:exported="true"
android:icon="@mipmap/themed_icon"
android:targetActivity=".GodotEditor">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity
android:name=".GodotGame"
android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"

View File

@@ -58,6 +58,7 @@ import org.godotengine.editor.embed.GameMenuFragment
import org.godotengine.editor.utils.signApk
import org.godotengine.editor.utils.verifyApk
import org.godotengine.godot.BuildConfig
import org.godotengine.godot.Godot
import org.godotengine.godot.GodotActivity
import org.godotengine.godot.GodotLib
import org.godotengine.godot.editor.utils.EditorUtils
@@ -158,6 +159,20 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
internal const val SNACKBAR_SHOW_DURATION_MS = 5000L
private const val PREF_KEY_DONT_SHOW_GAME_RESUME_HINT = "pref_key_dont_show_game_resume_hint"
@JvmStatic
fun isRunningInInstrumentation(): Boolean {
if (BuildConfig.BUILD_TYPE == "release") {
return false
}
return try {
Class.forName("org.godotengine.editor.GodotEditorTest")
true
} catch (_: ClassNotFoundException) {
false
}
}
}
internal val editorMessageDispatcher = EditorMessageDispatcher(this)
@@ -229,9 +244,15 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
enableEdgeToEdge()
}
// 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, getExcludedPermissions())
// 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() ||
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.
PermissionsUtil.requestManifestPermissions(this, getExcludedPermissions())
}
editorMessageDispatcher.parseStartIntent(packageManager, intent)
@@ -247,7 +268,7 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
override fun onNewIntent(newIntent: Intent) {
if (newIntent.hasCategory(HYBRID_APP_PANEL_CATEGORY) || newIntent.hasCategory(HYBRID_APP_IMMERSIVE_CATEGORY)) {
val params = newIntent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS)
val params = retrieveCommandLineParamsFromLaunchIntent(newIntent)
Log.d(TAG, "Received hybrid transition intent $newIntent with parameters ${params.contentToString()}")
// Override EXTRA_NEW_LAUNCH so the editor is not restarted
newIntent.putExtra(EXTRA_NEW_LAUNCH, false)
@@ -257,7 +278,7 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
var scene = ""
var xrMode = XR_MODE_DEFAULT
var path = ""
if (params != null) {
if (params.isNotEmpty()) {
val sceneIndex = params.indexOf(SCENE_ARG)
if (sceneIndex != -1 && sceneIndex + 1 < params.size) {
scene = params[sceneIndex +1]
@@ -511,6 +532,14 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
return editorWindowInfo.windowId
}
override fun onGodotForceQuit(instance: Godot) {
if (!isRunningInInstrumentation()) {
// For instrumented tests, we disable force-quitting to allow the tests to complete successfully, otherwise
// they fail when the process crashes.
super.onGodotForceQuit(instance)
}
}
final override fun onGodotForceQuit(godotInstanceId: Int): Boolean {
val editorWindowInfo = getEditorWindowInfoForInstanceId(godotInstanceId) ?: return super.onGodotForceQuit(godotInstanceId)

View File

@@ -63,20 +63,18 @@ abstract class BaseGodotGame: GodotEditor() {
// 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, getEditorGameEmbedMode())
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))
val launchingArgs = retrieveCommandLineParamsFromLaunchIntent()
val editorWindowInfo = retrieveEditorWindowInfo(launchingArgs, getEditorGameEmbedMode())
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()}")
Godot.getInstance(applicationContext).destroyAndKillProcess {
ProcessPhoenix.triggerRebirth(this, relaunchIntent)
}
return
Log.d(TAG, "Relaunching XR project using ${editorWindowInfo.windowClassName} with parameters ${launchingArgs.contentToString()}")
Godot.getInstance(applicationContext).destroyAndKillProcess {
ProcessPhoenix.triggerRebirth(this, relaunchIntent)
}
return
}
// Request project runtime permissions if necessary.