diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 81d982c4fd0..29b861859ae 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -2561,7 +2561,7 @@ Error EditorExportPlatformAndroid::run(const Ref &p_preset, print_verbose(output); if (err || rv != 0 || output.contains("Error: Activity not started")) { // The implicit launch failed, let's try an explicit launch by specifying the component name before giving up. - const String component_name = get_package_name(p_preset, package_name) + "/com.godot.game.GodotApp"; + const String component_name = get_package_name(p_preset, package_name) + "/com.godot.game.GodotAppLauncher"; print_line("Implicit launch failed... Trying explicit launch using", component_name); args.erase(get_package_name(p_preset, package_name)); args.push_back("-n"); diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp index d0dd3c2b484..289321f72cc 100644 --- a/platform/android/export/gradle_export_util.cpp +++ b/platform/android/export/gradle_export_util.cpp @@ -31,6 +31,7 @@ #include "gradle_export_util.h" #include "core/string/translation_server.h" +#include "modules/regex/regex.h" int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orientation) { switch (screen_orientation) { @@ -284,6 +285,19 @@ String _get_screen_sizes_tag(const Ref &p_preset) { } String _get_activity_tag(const Ref &p_export_platform, const Ref &p_preset, bool p_debug) { + String export_plugins_activity_element_contents; + Vector> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + for (int i = 0; i < export_plugins.size(); i++) { + if (export_plugins[i]->supports_platform(p_export_platform)) { + const String contents = export_plugins[i]->get_android_manifest_activity_element_contents(p_export_platform, p_debug); + if (!contents.is_empty()) { + export_plugins_activity_element_contents += contents; + export_plugins_activity_element_contents += "\n"; + } + } + } + + // Update the GodotApp activity tag. String orientation = _get_android_orientation_label(DisplayServer::ScreenOrientation(int(p_export_platform->get_project_setting(p_preset, "display/window/handheld/orientation")))); String manifest_activity_text = vformat( " &p_export_platform, con orientation, bool_to_string(bool(p_export_platform->get_project_setting(p_preset, "display/window/size/resizable")))); + // *LAUNCHER and *HOME categories should only go to the activity-alias. + Ref activity_content_to_remove_regex = RegEx::create_from_string(R"delim()delim"); + String updated_export_plugins_activity_element_contents = activity_content_to_remove_regex->sub(export_plugins_activity_element_contents, "", true); + manifest_activity_text += updated_export_plugins_activity_element_contents; + + manifest_activity_text += " \n"; + + // Update the GodotAppLauncher activity tag. + manifest_activity_text += " \n"; + manifest_activity_text += " \n" " \n" " \n"; @@ -317,18 +345,12 @@ String _get_activity_tag(const Ref &p_export_platform, con manifest_activity_text += " \n"; - Vector> export_plugins = EditorExport::get_singleton()->get_export_plugins(); - for (int i = 0; i < export_plugins.size(); i++) { - if (export_plugins[i]->supports_platform(p_export_platform)) { - const String contents = export_plugins[i]->get_android_manifest_activity_element_contents(p_export_platform, p_debug); - if (!contents.is_empty()) { - manifest_activity_text += contents; - manifest_activity_text += "\n"; - } - } - } + // Hybrid categories should only go to the actual 'GodotApp' activity. + Ref activity_alias_content_to_remove_regex = RegEx::create_from_string(R"delim()delim"); + String updated_export_plugins_activity_alias_element_contents = activity_alias_content_to_remove_regex->sub(export_plugins_activity_element_contents, "", true); + manifest_activity_text += updated_export_plugins_activity_alias_element_contents; - manifest_activity_text += " \n"; + manifest_activity_text += " \n"; return manifest_activity_text; } diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index d75a63f101a..c6cea8f31b2 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -35,9 +35,11 @@ configurations { dependencies { // Android instrumented test dependencies - androidTestImplementation "androidx.test.ext:junit:1.3.0" - androidTestImplementation "androidx.test.espresso:espresso-core:3.7.0" - androidTestImplementation "org.jetbrains.kotlin:kotlin-test:1.3.11" + androidTestImplementation "androidx.test.ext:junit:$versions.junitVersion" + androidTestImplementation "androidx.test.espresso:espresso-core:$versions.espressoCoreVersion" + androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$versions.kotlinTestVersion" + androidTestImplementation "androidx.test:runner:$versions.testRunnerVersion" + androidTestUtil "androidx.test:orchestrator:$versions.testOrchestratorVersion" implementation "androidx.fragment:fragment:$versions.fragmentVersion" implementation "androidx.core:core-splashscreen:$versions.splashscreenVersion" @@ -121,6 +123,15 @@ android { missingDimensionStrategy 'products', 'template' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + + // The following argument makes the Android Test Orchestrator run its + // "pm clear" command after each test invocation. This command ensures + // that the app's state is completely cleared between tests. + testInstrumentationRunnerArguments clearPackageData: 'true' + } + + testOptions { + execution 'ANDROIDX_TEST_ORCHESTRATOR' } lintOptions { diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index c2604f58ee5..51e179897b3 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -15,8 +15,12 @@ ext.versions = [ // Also update 'platform/android/detect.py#get_ndk_version()' when this is updated. ndkVersion : '28.1.13356709', splashscreenVersion: '1.0.1', - openxrVendorsVersion: '4.1.1-stable' - + openxrVendorsVersion: '4.1.1-stable', + junitVersion : '1.3.0', + espressoCoreVersion: '3.7.0', + kotlinTestVersion : '1.3.11', + testRunnerVersion : '1.7.0', + testOrchestratorVersion: '1.6.1', ] ext.getExportPackageName = { -> diff --git a/platform/android/java/app/src/androidTestInstrumented/java/com/godot/game/GodotAppTest.kt b/platform/android/java/app/src/androidTestInstrumented/java/com/godot/game/GodotAppTest.kt index f55954fe59b..3f0024e2a00 100644 --- a/platform/android/java/app/src/androidTestInstrumented/java/com/godot/game/GodotAppTest.kt +++ b/platform/android/java/app/src/androidTestInstrumented/java/com/godot/game/GodotAppTest.kt @@ -30,15 +30,19 @@ package com.godot.game +import android.content.ComponentName +import android.content.Intent import android.util.Log -import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.core.app.ActivityScenario import androidx.test.ext.junit.runners.AndroidJUnit4 import com.godot.game.test.GodotAppInstrumentedTestPlugin +import org.godotengine.godot.GodotActivity.Companion.EXTRA_COMMAND_LINE_PARAMS import org.godotengine.godot.plugin.GodotPluginRegistry -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import kotlin.test.assertEquals import kotlin.test.assertNotNull +import kotlin.test.assertNull import kotlin.test.assertTrue /** @@ -49,28 +53,94 @@ class GodotAppTest { companion object { private val TAG = GodotAppTest::class.java.simpleName - } - @get:Rule - val godotAppRule = ActivityScenarioRule(GodotApp::class.java) + private const val GODOT_APP_LAUNCHER_CLASS_NAME = "com.godot.game.GodotAppLauncher" + private const val GODOT_APP_CLASS_NAME = "com.godot.game.GodotApp" + + private val TEST_COMMAND_LINE_PARAMS = arrayOf("This is a test") + } /** * Runs the JavaClassWrapper tests via the GodotAppInstrumentedTestPlugin. */ @Test fun runJavaClassWrapperTests() { - val testPlugin = GodotPluginRegistry.getPluginRegistry() - .getPlugin("GodotAppInstrumentedTestPlugin") as GodotAppInstrumentedTestPlugin? - assertNotNull(testPlugin) + ActivityScenario.launch(GodotApp::class.java).use { scenario -> + scenario.onActivity { activity -> + val testPlugin = GodotPluginRegistry.getPluginRegistry() + .getPlugin("GodotAppInstrumentedTestPlugin") as GodotAppInstrumentedTestPlugin? + assertNotNull(testPlugin) - Log.d(TAG, "Waiting for the Godot main loop to start...") - testPlugin.waitForGodotMainLoopStarted() + Log.d(TAG, "Waiting for the Godot main loop to start...") + testPlugin.waitForGodotMainLoopStarted() - Log.d(TAG, "Running JavaClassWrapper tests...") - val result = testPlugin.runJavaClassWrapperTests() - assertNotNull(result) - result.exceptionOrNull()?.let { throw it } - assertTrue(result.isSuccess) - Log.d(TAG, "Passed ${result.getOrNull()} tests") + Log.d(TAG, "Running JavaClassWrapper tests...") + val result = testPlugin.runJavaClassWrapperTests() + assertNotNull(result) + result.exceptionOrNull()?.let { throw it } + assertTrue(result.isSuccess) + Log.d(TAG, "Passed ${result.getOrNull()} tests") + } + } + } + + /** + * Test implicit launch of the Godot app, and validates this resolves to the `GodotAppLauncher` activity alias. + */ + @Test + fun testImplicitGodotAppLauncherLaunch() { + val implicitLaunchIntent = Intent().apply { + setPackage(BuildConfig.APPLICATION_ID) + action = Intent.ACTION_MAIN + addCategory(Intent.CATEGORY_LAUNCHER) + putExtra(EXTRA_COMMAND_LINE_PARAMS, TEST_COMMAND_LINE_PARAMS) + } + ActivityScenario.launch(implicitLaunchIntent).use { scenario -> + scenario.onActivity { activity -> + assertEquals(activity.intent.component?.className, GODOT_APP_LAUNCHER_CLASS_NAME) + + val commandLineParams = activity.intent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS) + assertNull(commandLineParams) + } + } + } + + /** + * Test explicit launch of the Godot app via its activity-alias launcher, and validates it resolves properly. + */ + @Test + fun testExplicitGodotAppLauncherLaunch() { + val explicitIntent = Intent().apply { + component = ComponentName(BuildConfig.APPLICATION_ID, GODOT_APP_LAUNCHER_CLASS_NAME) + putExtra(EXTRA_COMMAND_LINE_PARAMS, TEST_COMMAND_LINE_PARAMS) + } + ActivityScenario.launch(explicitIntent).use { scenario -> + scenario.onActivity { activity -> + assertEquals(activity.intent.component?.className, GODOT_APP_LAUNCHER_CLASS_NAME) + + val commandLineParams = activity.intent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS) + assertNull(commandLineParams) + } + } + } + + /** + * Test explicit launch of the `GodotApp` activity. + */ + @Test + fun testExplicitGodotAppLaunch() { + val explicitIntent = Intent().apply { + component = ComponentName(BuildConfig.APPLICATION_ID, GODOT_APP_CLASS_NAME) + putExtra(EXTRA_COMMAND_LINE_PARAMS, TEST_COMMAND_LINE_PARAMS) + } + ActivityScenario.launch(explicitIntent).use { scenario -> + scenario.onActivity { activity -> + assertEquals(activity.intent.component?.className, GODOT_APP_CLASS_NAME) + + val commandLineParams = activity.intent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS) + assertNotNull(commandLineParams) + assertTrue(commandLineParams.contentEquals(TEST_COMMAND_LINE_PARAMS)) + } + } } } diff --git a/platform/android/java/app/src/main/AndroidManifest.xml b/platform/android/java/app/src/main/AndroidManifest.xml index 1c5dedf03e2..41774516a40 100644 --- a/platform/android/java/app/src/main/AndroidManifest.xml +++ b/platform/android/java/app/src/main/AndroidManifest.xml @@ -31,23 +31,25 @@ - + tools:ignore="UnusedAttribute" /> + - + diff --git a/platform/android/java/editor/build.gradle b/platform/android/java/editor/build.gradle index ef846b270cd..bbe9227a9f8 100644 --- a/platform/android/java/editor/build.gradle +++ b/platform/android/java/editor/build.gradle @@ -91,6 +91,17 @@ android { ] ndk { debugSymbolLevel 'NONE' } + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + + // The following argument makes the Android Test Orchestrator run its + // "pm clear" command after each test invocation. This command ensures + // that the app's state is completely cleared between tests. + testInstrumentationRunnerArguments clearPackageData: 'true' + } + + testOptions { + execution 'ANDROIDX_TEST_ORCHESTRATOR' } base { @@ -196,4 +207,11 @@ dependencies { horizonosImplementation "org.godotengine:godot-openxr-vendors-meta:$versions.openxrVendorsVersion" // Pico dependencies picoosImplementation "org.godotengine:godot-openxr-vendors-pico:$versions.openxrVendorsVersion" + + // Android instrumented test dependencies + androidTestImplementation "androidx.test.ext:junit:$versions.junitVersion" + androidTestImplementation "androidx.test.espresso:espresso-core:$versions.espressoCoreVersion" + androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$versions.kotlinTestVersion" + androidTestImplementation "androidx.test:runner:$versions.testRunnerVersion" + androidTestUtil "androidx.test:orchestrator:$versions.testOrchestratorVersion" } diff --git a/platform/android/java/editor/src/androidTest/java/org/godotengine/editor/GodotEditorTest.kt b/platform/android/java/editor/src/androidTest/java/org/godotengine/editor/GodotEditorTest.kt new file mode 100644 index 00000000000..c421c21d5a2 --- /dev/null +++ b/platform/android/java/editor/src/androidTest/java/org/godotengine/editor/GodotEditorTest.kt @@ -0,0 +1,117 @@ +/**************************************************************************/ +/* GodotEditorTest.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 android.content.ComponentName +import android.content.Intent +import androidx.test.core.app.ActivityScenario +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.godotengine.godot.GodotActivity.Companion.EXTRA_COMMAND_LINE_PARAMS +import org.junit.Test +import org.junit.runner.RunWith +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue + +/** + * Instrumented test for the Godot editor. + */ +@RunWith(AndroidJUnit4::class) +class GodotEditorTest { + companion object { + private val TAG = GodotEditorTest::class.simpleName + + private val TEST_COMMAND_LINE_PARAMS = arrayOf("This is a test") + private const val PROJECT_MANAGER_CLASS_NAME = "org.godotengine.editor.ProjectManager" + private const val GODOT_EDITOR_CLASS_NAME = "org.godotengine.editor.GodotEditor" + } + + /** + * Implicitly launch the project manager. + */ + @Test + fun testImplicitProjectManagerLaunch() { + val implicitLaunchIntent = Intent().apply { + setPackage(BuildConfig.APPLICATION_ID) + action = Intent.ACTION_MAIN + addCategory(Intent.CATEGORY_LAUNCHER) + putExtra(EXTRA_COMMAND_LINE_PARAMS, TEST_COMMAND_LINE_PARAMS) + } + ActivityScenario.launch(implicitLaunchIntent).use { scenario -> + scenario.onActivity { activity -> + assertEquals(activity.intent.component?.className, PROJECT_MANAGER_CLASS_NAME) + + val commandLineParams = activity.intent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS) + assertNull(commandLineParams) + } + } + } + + /** + * Explicitly launch the project manager. + */ + @Test + fun testExplicitProjectManagerLaunch() { + val explicitProjectManagerIntent = Intent().apply { + component = ComponentName(BuildConfig.APPLICATION_ID, PROJECT_MANAGER_CLASS_NAME) + putExtra(EXTRA_COMMAND_LINE_PARAMS, TEST_COMMAND_LINE_PARAMS) + } + ActivityScenario.launch(explicitProjectManagerIntent).use { scenario -> + scenario.onActivity { activity -> + assertEquals(activity.intent.component?.className, PROJECT_MANAGER_CLASS_NAME) + + val commandLineParams = activity.intent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS) + assertNull(commandLineParams) + } + } + } + + /** + * Explicitly launch the `GodotEditor` activity. + */ + @Test + fun testExplicitGodotEditorLaunch() { + val godotEditorIntent = Intent().apply { + component = ComponentName(BuildConfig.APPLICATION_ID, GODOT_EDITOR_CLASS_NAME) + putExtra(EXTRA_COMMAND_LINE_PARAMS, TEST_COMMAND_LINE_PARAMS) + } + ActivityScenario.launch(godotEditorIntent).use { scenario -> + scenario.onActivity { activity -> + assertEquals(activity.intent.component?.className, GODOT_EDITOR_CLASS_NAME) + + val commandLineParams = activity.intent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS) + assertNotNull(commandLineParams) + assertTrue(commandLineParams.contentEquals(TEST_COMMAND_LINE_PARAMS)) + } + } + } +} diff --git a/platform/android/java/editor/src/horizonos/AndroidManifest.xml b/platform/android/java/editor/src/horizonos/AndroidManifest.xml index b39e8b043b9..da909865459 100644 --- a/platform/android/java/editor/src/horizonos/AndroidManifest.xml +++ b/platform/android/java/editor/src/horizonos/AndroidManifest.xml @@ -47,20 +47,31 @@ - + + + + + + + + @@ -54,13 +54,6 @@ android:defaultWidth="@dimen/editor_default_window_width" android:defaultHeight="@dimen/editor_default_window_height" /> - - - - - - - @@ -77,6 +70,18 @@ + + + + + + + + = 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) diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotGame.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotGame.kt index 27615d9e5c5..392c26b8743 100644 --- a/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotGame.kt +++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotGame.kt @@ -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. diff --git a/platform/android/java/editor/src/picoos/AndroidManifest.xml b/platform/android/java/editor/src/picoos/AndroidManifest.xml index 554fec3ebb1..37f160ce6a0 100644 --- a/platform/android/java/editor/src/picoos/AndroidManifest.xml +++ b/platform/android/java/editor/src/picoos/AndroidManifest.xml @@ -16,16 +16,10 @@ - - - - - - + tools:replace="android:screenOrientation"/> { + val targetComponent = launchIntent.component ?: componentName + val activityInfo = packageManager.getActivityInfo(targetComponent, 0) + if (!activityInfo.exported) { + val params = launchIntent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS) + return params ?: emptyArray() + } + return emptyArray() + } + @CallSuper override fun onCreate(savedInstanceState: Bundle?) { + intent = sanitizeLaunchIntent(intent) + val assetsCommandLine = try { CommandLineFileParser.parseCommandLine(assets.open("_cl_")) - } catch (ignored: Exception) { + } catch (_: Exception) { mutableListOf() } + Log.d(TAG, "Project command line parameters: $assetsCommandLine") commandLineParams.addAll(assetsCommandLine) - val params = intent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS) - Log.d(TAG, "Starting intent $intent with parameters ${params.contentToString()}") - commandLineParams.addAll(params ?: emptyArray()) + val intentCommandLine = retrieveCommandLineParamsFromLaunchIntent() + Log.d(TAG, "Launch intent $intent with parameters ${intentCommandLine.contentToString()}") + commandLineParams.addAll(intentCommandLine) super.onCreate(savedInstanceState) @@ -167,10 +197,9 @@ abstract class GodotActivity : FragmentActivity(), GodotHost { } override fun onNewIntent(newIntent: Intent) { - super.onNewIntent(newIntent) - intent = newIntent - - handleStartIntent(newIntent, false) + intent = sanitizeLaunchIntent(newIntent) + super.onNewIntent(intent) + handleStartIntent(intent, false) } private fun handleStartIntent(intent: Intent, newLaunch: Boolean) {