1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-05 12:10:55 +00:00

Add support for embedding game process in the Android Editor

- Implement Android editor specific `EmbeddedGodotGame` to support embedding the game window in the Android editor
This commit is contained in:
Fredia Huya-Kouadio
2025-01-07 21:31:53 -08:00
parent 296de7da83
commit 7495a8a02e
71 changed files with 2497 additions and 301 deletions

View File

@@ -333,7 +333,7 @@ class Godot(private val context: Context) {
* Toggle immersive mode.
* Must be called from the UI thread.
*/
private fun enableImmersiveMode(enabled: Boolean, override: Boolean = false) {
fun enableImmersiveMode(enabled: Boolean, override: Boolean = false) {
val activity = getActivity() ?: return
val window = activity.window ?: return
@@ -1068,6 +1068,16 @@ class Godot(private val context: Context) {
return PermissionsUtil.getGrantedPermissions(getActivity())
}
/**
* Returns true if this is the Godot editor.
*/
fun isEditorHint() = isEditorBuild() && GodotLib.isEditorHint()
/**
* Returns true if this is the Godot project manager.
*/
fun isProjectManagerHint() = isEditorBuild() && GodotLib.isProjectManagerHint()
/**
* Return true if the given feature is supported.
*/
@@ -1177,4 +1187,9 @@ class Godot(private val context: Context) {
val verifyResult = primaryHost?.verifyApk(apkPath) ?: Error.ERR_UNAVAILABLE
return verifyResult.toNativeValue()
}
@Keep
private fun nativeOnEditorWorkspaceSelected(workspace: String) {
primaryHost?.onEditorWorkspaceSelected(workspace)
}
}

View File

@@ -509,4 +509,11 @@ public class GodotFragment extends Fragment implements IDownloaderClient, GodotH
}
return false;
}
@Override
public void onEditorWorkspaceSelected(String workspace) {
if (parentHost != null) {
parentHost.onEditorWorkspaceSelected(workspace);
}
}
}

View File

@@ -145,4 +145,9 @@ public interface GodotHost {
default boolean supportsFeature(String featureTag) {
return false;
}
/**
* Invoked on the render thread when an editor workspace has been selected.
*/
default void onEditorWorkspaceSelected(String workspace) {}
}

View File

@@ -196,6 +196,30 @@ public class GodotLib {
*/
public static native String getEditorSetting(String settingKey);
/**
* Update the 'key' editor setting with the given data. Must be called on the render thread.
* @param key
* @param data
*/
public static native void setEditorSetting(String key, Object data);
/**
* Used to access project metadata from the editor settings. Must be accessed on the render thread.
* @param section
* @param key
* @param defaultValue
* @return
*/
public static native Object getEditorProjectMetadata(String section, String key, Object defaultValue);
/**
* Set the project metadata to the editor settings. Must be accessed on the render thread.
* @param section
* @param key
* @param data
*/
public static native void setEditorProjectMetadata(String section, String key, Object data);
/**
* Invoke method |p_method| on the Godot object specified by |p_id|
* @param p_id Id of the Godot object to invoke
@@ -267,4 +291,8 @@ public class GodotLib {
* @return the project resource directory
*/
public static native String getProjectResourceDir();
static native boolean isEditorHint();
static native boolean isProjectManagerHint();
}

View File

@@ -52,7 +52,7 @@ import kotlin.math.abs
/**
* Utility class for managing dialogs.
*/
internal class DialogUtils {
class DialogUtils {
companion object {
private val TAG = DialogUtils::class.java.simpleName
@@ -79,7 +79,7 @@ internal class DialogUtils {
* @param message The message displayed in the dialog.
* @param buttons An array of button labels to display.
*/
fun showDialog(activity: Activity, title: String, message: String, buttons: Array<String>) {
internal fun showDialog(activity: Activity, title: String, message: String, buttons: Array<String>) {
var dismissDialog: () -> Unit = {} // Helper to dismiss the Dialog when a button is clicked.
activity.runOnUiThread {
val builder = AlertDialog.Builder(activity)
@@ -174,7 +174,7 @@ internal class DialogUtils {
* @param message The message displayed in the input dialog.
* @param existingText The existing text that will be pre-filled in the input field.
*/
fun showInputDialog(activity: Activity, title: String, message: String, existingText: String) {
internal fun showInputDialog(activity: Activity, title: String, message: String, existingText: String) {
val inputField = EditText(activity)
val paddingHorizontal = activity.resources.getDimensionPixelSize(R.dimen.dialog_padding_horizontal)
val paddingVertical = activity.resources.getDimensionPixelSize(R.dimen.dialog_padding_vertical)

View File

@@ -0,0 +1,117 @@
/**************************************************************************/
/* GameMenuUtils.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.godot.utils
import android.util.Log
import org.godotengine.godot.GodotLib
/**
* Utility class for accessing and using game menu APIs.
*/
object GameMenuUtils {
private val TAG = GameMenuUtils::class.java.simpleName
/**
* Enum representing the "run/window_placement/game_embed_mode" editor settings.
*/
enum class GameEmbedMode(internal val nativeValue: Int) {
DISABLED(-1), AUTO(0), ENABLED(1);
companion object {
internal const val SETTING_KEY = "run/window_placement/game_embed_mode"
@JvmStatic
internal fun fromNativeValue(nativeValue: Int): GameEmbedMode? {
for (mode in GameEmbedMode.entries) {
if (mode.nativeValue == nativeValue) {
return mode
}
}
return null
}
}
}
@JvmStatic
external fun setSuspend(enabled: Boolean)
@JvmStatic
external fun nextFrame()
@JvmStatic
external fun setNodeType(type: Int)
@JvmStatic
external fun setSelectMode(mode: Int)
@JvmStatic
external fun setSelectionVisible(visible: Boolean)
@JvmStatic
external fun setCameraOverride(enabled: Boolean)
@JvmStatic
external fun setCameraManipulateMode(mode: Int)
@JvmStatic
external fun resetCamera2DPosition()
@JvmStatic
external fun resetCamera3DPosition()
@JvmStatic
external fun playMainScene()
/**
* Returns [GameEmbedMode] stored in the editor settings.
*
* Must be called on the render thread.
*/
fun fetchGameEmbedMode(): GameEmbedMode {
try {
val gameEmbedModeValue = Integer.parseInt(GodotLib.getEditorSetting(GameEmbedMode.SETTING_KEY))
val gameEmbedMode = GameEmbedMode.fromNativeValue(gameEmbedModeValue) ?: GameEmbedMode.AUTO
return gameEmbedMode
} catch (e: Exception) {
Log.w(TAG, "Unable to retrieve game embed mode", e)
return GameEmbedMode.AUTO
}
}
/**
* Update the 'game_embed_mode' editor setting.
*
* Must be called on the render thread.
*/
fun saveGameEmbedMode(gameEmbedMode: GameEmbedMode) {
GodotLib.setEditorSetting(GameEmbedMode.SETTING_KEY, gameEmbedMode.nativeValue)
}
}