Skip to content

Commit

Permalink
Add support for embedding game process in the Android Editor
Browse files Browse the repository at this point in the history
- Add support for using an Android Service to host the Godot engine
- Provide an abstract `GodotService` implementation which can be extended to build an Android Service that host an instance of the Godot engine
- Implement editor-specific `EmbeddedGameService` (extending from `GodotService`) to support embedding the game process in the Android editor
- Implement `EmbeddedGameFragment` to provide the view and logic to wrap `EmbeddedGameService`
  • Loading branch information
m4gr3d committed Jan 13, 2025
1 parent d79ff84 commit 4bc3f5a
Show file tree
Hide file tree
Showing 18 changed files with 879 additions and 131 deletions.
2 changes: 1 addition & 1 deletion doc/classes/DisplayServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1943,7 +1943,7 @@
Display server supports [constant WINDOW_FLAG_EXCLUDE_FROM_CAPTURE] window flag.
</constant>
<constant name="FEATURE_WINDOW_EMBEDDING" value="29" enum="Feature">
Display server supports embedding a window from another process. [b]Windows, Linux (X11)[/b]
Display server supports embedding a window from another process. [b]Android, Windows, Linux (X11)[/b]
</constant>
<constant name="FEATURE_NATIVE_DIALOG_FILE_MIME" value="30" enum="Feature">
Native file selection dialog supports MIME types as filters.
Expand Down
8 changes: 8 additions & 0 deletions platform/android/java/editor/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@
android:defaultWidth="@dimen/editor_default_window_width"
android:defaultHeight="@dimen/editor_default_window_height" />
</activity>

<service
android:name=".embed.EmbeddedGameService"
android:label="@string/godot_embedded_game_service_name"
android:process=":EmbeddedGame"
android:description="@string/godot_embedded_game_service_description"
android:exported="false"
android:icon="@mipmap/ic_play_window"/>
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import android.widget.Toast
import androidx.annotation.CallSuper
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.window.layout.WindowMetricsCalculator
import org.godotengine.editor.embed.EmbeddedGameFragment
import org.godotengine.editor.utils.signApk
import org.godotengine.editor.utils.verifyApk
import org.godotengine.godot.GodotActivity
Expand Down Expand Up @@ -119,6 +120,8 @@ abstract class BaseGodotEditor : GodotActivity() {
private val commandLineParams = ArrayList<String>()
private val editorLoadingIndicator: View? by lazy { findViewById(R.id.editor_loading_indicator) }

private var embeddedGameFragment: EmbeddedGameFragment? = null

override fun getGodotAppLayout() = R.layout.godot_editor_layout

internal open fun getEditorWindowInfo() = EDITOR_MAIN_INFO
Expand Down Expand Up @@ -169,6 +172,21 @@ abstract class BaseGodotEditor : GodotActivity() {
}

super.onCreate(savedInstanceState)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val embeddedFragment =
supportFragmentManager.findFragmentById(R.id.godot_embedded_game_container)
if (embeddedFragment is EmbeddedGameFragment) {
Log.v(TAG, "Reusing existing embedded game fragment instance.")
embeddedGameFragment = embeddedFragment
} else {
Log.v(TAG, "Creating new embedded game fragment instance.")
embeddedGameFragment = EmbeddedGameFragment()
supportFragmentManager.beginTransaction()
.replace(R.id.godot_embedded_game_container, embeddedGameFragment!!, EmbeddedGameFragment.TAG)
.commitNowAllowingStateLoss()
}
}
}

override fun onGodotSetupCompleted() {
Expand Down Expand Up @@ -283,10 +301,15 @@ abstract class BaseGodotEditor : GodotActivity() {
return newInstance
}

override fun onNewGodotInstanceRequested(args: Array<String>): Int {
final override fun onNewGodotInstanceRequested(args: Array<String>): Int {
val editorWindowInfo = retrieveEditorWindowInfo(args)
val shouldRunGameEmbedded = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && true // TODO: Add logic behind it
if (editorWindowInfo == RUN_GAME_INFO && shouldRunGameEmbedded) {
embeddedGameFragment?.startEmbeddedGame(args)
return EmbeddedGameFragment.WINDOW_ID
}

// Launch a new activity
// We're not running embedded so launch a new activity
val sourceView = godotFragment?.view
val activityOptions = if (sourceView == null) {
null
Expand Down Expand Up @@ -317,6 +340,11 @@ abstract class BaseGodotEditor : GodotActivity() {
}

final override fun onGodotForceQuit(godotInstanceId: Int): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && godotInstanceId == EmbeddedGameFragment.WINDOW_ID) {
embeddedGameFragment?.stopEmbeddedGame()
return true
}

val editorWindowInfo = getEditorWindowInfoForInstanceId(godotInstanceId) ?: return super.onGodotForceQuit(godotInstanceId)

if (editorWindowInfo.windowClassName == javaClass.name) {
Expand Down
Loading

0 comments on commit 4bc3f5a

Please sign in to comment.