Merge pull request #92704 from m4gr3d/update_android_editor_activity_layout

Consolidate the ProjectManager and Editor windows into a single Android Activity class
This commit is contained in:
Rémi Verschelde 2024-06-04 10:10:08 +02:00
commit 62b15238e5
No known key found for this signature in database
GPG Key ID: C3336907360768E1
3 changed files with 101 additions and 100 deletions

View File

@ -32,13 +32,11 @@
android:requestLegacyExternalStorage="true"> android:requestLegacyExternalStorage="true">
<activity <activity
android:name=".GodotProjectManager" android:name=".GodotEditor"
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode" android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
android:launchMode="singleTask" android:launchMode="singleTask"
android:screenOrientation="userLandscape" android:screenOrientation="userLandscape"
android:exported="true" android:exported="true">
android:process=":GodotProjectManager">
<layout android:defaultHeight="@dimen/editor_default_window_height" <layout android:defaultHeight="@dimen/editor_default_window_height"
android:defaultWidth="@dimen/editor_default_window_width" /> android:defaultWidth="@dimen/editor_default_window_width" />
@ -49,17 +47,6 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".GodotEditor"
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
android:process=":GodotEditor"
android:launchMode="singleTask"
android:screenOrientation="userLandscape"
android:exported="false">
<layout android:defaultHeight="@dimen/editor_default_window_height"
android:defaultWidth="@dimen/editor_default_window_width" />
</activity>
<activity <activity
android:name=".GodotGame" android:name=".GodotGame"
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode" android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"

View File

@ -1,5 +1,5 @@
/**************************************************************************/ /**************************************************************************/
/* GodotProjectManager.kt */ /* EditorWindowInfo.kt */
/**************************************************************************/ /**************************************************************************/
/* This file is part of: */ /* This file is part of: */
/* GODOT ENGINE */ /* GODOT ENGINE */
@ -31,14 +31,38 @@
package org.godotengine.editor package org.godotengine.editor
/** /**
* Launcher activity for the Godot Android Editor. * Specifies the policy for adjacent launches.
*
* It presents the user with the project manager interface.
* Upon selection of a project, this activity (via its parent logic) starts the
* [GodotEditor] activity.
*/ */
class GodotProjectManager : GodotEditor() { enum class LaunchAdjacentPolicy {
override fun checkForProjectPermissionsToEnable() { /**
// Nothing to do here.. we have yet to select a project to load. * Adjacent launches are disabled.
*/
DISABLED,
/**
* Adjacent launches are enabled / disabled based on the device and screen metrics.
*/
AUTO,
/**
* Adjacent launches are enabled.
*/
ENABLED
} }
/**
* Describe the editor window to launch
*/
data class EditorWindowInfo(
val windowClassName: String,
val windowId: Int,
val processNameSuffix: String,
val launchAdjacentPolicy: LaunchAdjacentPolicy = LaunchAdjacentPolicy.DISABLED
) {
constructor(
windowClass: Class<*>,
windowId: Int,
processNameSuffix: String,
launchAdjacentPolicy: LaunchAdjacentPolicy = LaunchAdjacentPolicy.DISABLED
) : this(windowClass.name, windowId, processNameSuffix, launchAdjacentPolicy)
} }

View File

@ -32,12 +32,14 @@ package org.godotengine.editor
import android.Manifest import android.Manifest
import android.app.ActivityManager import android.app.ActivityManager
import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.* import android.os.*
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.annotation.CallSuper
import androidx.window.layout.WindowMetricsCalculator import androidx.window.layout.WindowMetricsCalculator
import org.godotengine.godot.GodotActivity import org.godotengine.godot.GodotActivity
import org.godotengine.godot.GodotLib import org.godotengine.godot.GodotLib
@ -64,18 +66,15 @@ open class GodotEditor : GodotActivity() {
private const val EXTRA_COMMAND_LINE_PARAMS = "command_line_params" private const val EXTRA_COMMAND_LINE_PARAMS = "command_line_params"
private const val EDITOR_ID = 777 // Command line arguments
private const val EDITOR_ARG = "--editor" private const val EDITOR_ARG = "--editor"
private const val EDITOR_ARG_SHORT = "-e" private const val EDITOR_ARG_SHORT = "-e"
private const val EDITOR_PROCESS_NAME_SUFFIX = ":GodotEditor" private const val EDITOR_PROJECT_MANAGER_ARG = "--project-manager"
private const val EDITOR_PROJECT_MANAGER_ARG_SHORT = "-p"
private const val GAME_ID = 667 // Info for the various classes used by the editor
private const val GAME_PROCESS_NAME_SUFFIX = ":GodotGame" internal val EDITOR_MAIN_INFO = EditorWindowInfo(GodotEditor::class.java, 777, "")
internal val RUN_GAME_INFO = EditorWindowInfo(GodotGame::class.java, 667, ":GodotGame", LaunchAdjacentPolicy.AUTO)
private const val PROJECT_MANAGER_ID = 555
private const val PROJECT_MANAGER_ARG = "--project-manager"
private const val PROJECT_MANAGER_ARG_SHORT = "-p"
private const val PROJECT_MANAGER_PROCESS_NAME_SUFFIX = ":GodotProjectManager"
/** /**
* Sets of constants to specify the window to use to run the project. * Sets of constants to specify the window to use to run the project.
@ -96,8 +95,8 @@ open class GodotEditor : GodotActivity() {
PermissionsUtil.requestManifestPermissions(this, setOf(Manifest.permission.RECORD_AUDIO)) PermissionsUtil.requestManifestPermissions(this, setOf(Manifest.permission.RECORD_AUDIO))
val params = intent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS) val params = intent.getStringArrayExtra(EXTRA_COMMAND_LINE_PARAMS)
Log.d(TAG, "Received parameters ${params.contentToString()}") Log.d(TAG, "Starting intent $intent with parameters ${params.contentToString()}")
updateCommandLineParams(params) updateCommandLineParams(params?.asList() ?: emptyList())
if (BuildConfig.BUILD_TYPE == "dev" && WAIT_FOR_DEBUGGER) { if (BuildConfig.BUILD_TYPE == "dev" && WAIT_FOR_DEBUGGER) {
Debug.waitForDebugger() Debug.waitForDebugger()
@ -133,98 +132,85 @@ open class GodotEditor : GodotActivity() {
} }
} }
private fun updateCommandLineParams(args: Array<String>?) { @CallSuper
protected open fun updateCommandLineParams(args: List<String>) {
// Update the list of command line params with the new args // Update the list of command line params with the new args
commandLineParams.clear() commandLineParams.clear()
if (!args.isNullOrEmpty()) { if (args.isNotEmpty()) {
commandLineParams.addAll(listOf(*args)) commandLineParams.addAll(args)
} }
if (BuildConfig.BUILD_TYPE == "dev") { if (BuildConfig.BUILD_TYPE == "dev") {
commandLineParams.add("--benchmark") commandLineParams.add("--benchmark")
} }
} }
override fun getCommandLine() = commandLineParams final override fun getCommandLine() = commandLineParams
protected open fun getEditorWindowInfo(args: Array<String>): EditorWindowInfo {
var hasEditor = 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
}
}
return if (hasEditor) {
EDITOR_MAIN_INFO
} else {
RUN_GAME_INFO
}
}
protected open fun getEditorWindowInfoForInstanceId(instanceId: Int): EditorWindowInfo? {
return when (instanceId) {
RUN_GAME_INFO.windowId -> RUN_GAME_INFO
EDITOR_MAIN_INFO.windowId -> EDITOR_MAIN_INFO
else -> null
}
}
override fun onNewGodotInstanceRequested(args: Array<String>): Int { override fun onNewGodotInstanceRequested(args: Array<String>): Int {
// Parse the arguments to figure out which activity to start. val editorWindowInfo = getEditorWindowInfo(args)
var targetClass: Class<*> = GodotGame::class.java
var instanceId = GAME_ID
// Whether we should launch the new godot instance in an adjacent window
// https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_LAUNCH_ADJACENT
var launchAdjacent = shouldGameLaunchAdjacent()
for (arg in args) {
if (EDITOR_ARG == arg || EDITOR_ARG_SHORT == arg) {
targetClass = GodotEditor::class.java
launchAdjacent = false
instanceId = EDITOR_ID
break
}
if (PROJECT_MANAGER_ARG == arg || PROJECT_MANAGER_ARG_SHORT == arg) {
targetClass = GodotProjectManager::class.java
launchAdjacent = false
instanceId = PROJECT_MANAGER_ID
break
}
}
// Launch a new activity // Launch a new activity
val newInstance = Intent(this, targetClass) val newInstance = Intent()
.setComponent(ComponentName(this, editorWindowInfo.windowClassName))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(EXTRA_COMMAND_LINE_PARAMS, args) .putExtra(EXTRA_COMMAND_LINE_PARAMS, args)
if (launchAdjacent) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (editorWindowInfo.launchAdjacentPolicy == LaunchAdjacentPolicy.ENABLED ||
(editorWindowInfo.launchAdjacentPolicy == LaunchAdjacentPolicy.AUTO && shouldGameLaunchAdjacent())) {
Log.v(TAG, "Adding flag for adjacent launch")
newInstance.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT) newInstance.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT)
} }
if (targetClass == javaClass) { }
Log.d(TAG, "Restarting $targetClass with parameters ${args.contentToString()}") if (editorWindowInfo.windowClassName == javaClass.name) {
Log.d(TAG, "Restarting ${editorWindowInfo.windowClassName} with parameters ${args.contentToString()}")
ProcessPhoenix.triggerRebirth(this, newInstance) ProcessPhoenix.triggerRebirth(this, newInstance)
} else { } else {
Log.d(TAG, "Starting $targetClass with parameters ${args.contentToString()}") Log.d(TAG, "Starting ${editorWindowInfo.windowClassName} with parameters ${args.contentToString()}")
newInstance.putExtra(EXTRA_NEW_LAUNCH, true) newInstance.putExtra(EXTRA_NEW_LAUNCH, true)
startActivity(newInstance) startActivity(newInstance)
} }
return instanceId return editorWindowInfo.windowId
} }
override fun onGodotForceQuit(godotInstanceId: Int): Boolean { final override fun onGodotForceQuit(godotInstanceId: Int): Boolean {
val targetClass: Class<*>? val editorWindowInfo = getEditorWindowInfoForInstanceId(godotInstanceId) ?: return super.onGodotForceQuit(godotInstanceId)
val processNameSuffix: String
when (godotInstanceId) {
GAME_ID -> {
processNameSuffix = GAME_PROCESS_NAME_SUFFIX
targetClass = GodotGame::class.java
}
EDITOR_ID -> {
processNameSuffix = EDITOR_PROCESS_NAME_SUFFIX
targetClass = GodotEditor::class.java
}
PROJECT_MANAGER_ID -> {
processNameSuffix = PROJECT_MANAGER_PROCESS_NAME_SUFFIX
targetClass = GodotProjectManager::class.java
}
else -> {
processNameSuffix = ""
targetClass = null
}
}
if (targetClass == javaClass) { if (editorWindowInfo.windowClassName == javaClass.name) {
Log.d(TAG, "Force quitting $targetClass") Log.d(TAG, "Force quitting ${editorWindowInfo.windowClassName}")
ProcessPhoenix.forceQuit(this) ProcessPhoenix.forceQuit(this)
return true return true
} }
if (processNameSuffix.isBlank()) { val processName = packageName + editorWindowInfo.processNameSuffix
return false
}
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val runningProcesses = activityManager.runningAppProcesses val runningProcesses = activityManager.runningAppProcesses
for (runningProcess in runningProcesses) { for (runningProcess in runningProcesses) {
if (runningProcess.processName.endsWith(processNameSuffix)) { if (runningProcess.processName == processName) {
// Killing process directly // Killing process directly
Log.v(TAG, "Killing Godot process ${runningProcess.processName}") Log.v(TAG, "Killing Godot process ${runningProcess.processName}")
Process.killProcess(runningProcess.pid) Process.killProcess(runningProcess.pid)
@ -232,11 +218,11 @@ open class GodotEditor : GodotActivity() {
} }
} }
return false return super.onGodotForceQuit(godotInstanceId)
} }
// Get the screen's density scale // Get the screen's density scale
protected val isLargeScreen: Boolean private val isLargeScreen: Boolean
// Get the minimum window size // Correspond to the EXPANDED window size class. // Get the minimum window size // Correspond to the EXPANDED window size class.
get() { get() {
val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this) val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this)
@ -273,6 +259,10 @@ open class GodotEditor : GodotActivity() {
protected open fun enablePanAndScaleGestures() = protected open fun enablePanAndScaleGestures() =
java.lang.Boolean.parseBoolean(GodotLib.getEditorSetting("interface/touchscreen/enable_pan_and_scale_gestures")) java.lang.Boolean.parseBoolean(GodotLib.getEditorSetting("interface/touchscreen/enable_pan_and_scale_gestures"))
/**
* Whether we should launch the new godot instance in an adjacent window
* @see https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_LAUNCH_ADJACENT
*/
private fun shouldGameLaunchAdjacent(): Boolean { private fun shouldGameLaunchAdjacent(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
try { try {