diff --git a/Source/Android/app/src/main/AndroidManifest.xml b/Source/Android/app/src/main/AndroidManifest.xml
index e68fa9f757..96876acd46 100644
--- a/Source/Android/app/src/main/AndroidManifest.xml
+++ b/Source/Android/app/src/main/AndroidManifest.xml
@@ -138,6 +138,11 @@
android:exported="false"
android:theme="@style/Theme.Dolphin.Main" />
+
+
(CONFLATED)
+ val goBack = _goBack.receiveAsFlow()
+
+ init {
+ if (!Netplay.isClientConnected()) {
+ _goBack.trySend(Unit)
+ }
+ }
+
+ @OptIn(DelicateCoroutinesApi::class)
+ override fun onCleared() {
+ super.onCleared()
+ GlobalScope.launch {
+ Netplay.quit()
+ }
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayActivity.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayActivity.kt
new file mode 100644
index 0000000000..7f199bd45e
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayActivity.kt
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.netplay.ui
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.flowWithLifecycle
+import androidx.lifecycle.lifecycleScope
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import org.dolphinemu.dolphinemu.activities.EmulationActivity
+import org.dolphinemu.dolphinemu.features.netplay.Netplay
+import org.dolphinemu.dolphinemu.features.netplay.model.NetplayViewModel
+import org.dolphinemu.dolphinemu.ui.main.ThemeProvider
+import org.dolphinemu.dolphinemu.ui.theme.DolphinTheme
+import org.dolphinemu.dolphinemu.utils.ThemeHelper
+
+class NetplayActivity : AppCompatActivity(), ThemeProvider {
+ override var themeId: Int = 0
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ ThemeHelper.setTheme(this)
+ enableEdgeToEdge()
+ super.onCreate(savedInstanceState)
+
+ val viewModel = ViewModelProvider(this)[NetplayViewModel::class.java]
+
+ viewModel.goBack
+ .onEach { finish() }
+ .launchIn(lifecycleScope)
+
+ viewModel.launchGame
+ .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
+ .onEach { EmulationActivity.launch(this, it, false) }
+ .launchIn(lifecycleScope)
+
+ setContent {
+ DolphinTheme {
+ NetplayScreen(
+ onBackClicked = { finish() },
+ )
+ }
+ }
+ }
+
+ override fun setTheme(themeId: Int) {
+ super.setTheme(themeId)
+ this.themeId = themeId
+ }
+
+ override fun onResume() {
+ ThemeHelper.setCorrectTheme(this)
+ super.onResume()
+ }
+
+ companion object {
+ @JvmStatic
+ fun launch(context: Context) {
+ context.startActivity(Intent(context, NetplayActivity::class.java))
+ }
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayScreen.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayScreen.kt
new file mode 100644
index 0000000000..de3b89d6c1
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayScreen.kt
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.netplay.ui
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MediumTopAppBar
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import org.dolphinemu.dolphinemu.R
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun NetplayScreen(
+ onBackClicked: () -> Unit,
+) {
+ Scaffold(
+ topBar = {
+ MediumTopAppBar(
+ title = { Text(stringResource(R.string.netplay_title)) },
+ navigationIcon = {
+ IconButton(onClick = onBackClicked) {
+ Icon(
+ Icons.AutoMirrored.Filled.ArrowBack,
+ contentDescription = "Back"
+ )
+ }
+ },
+ )
+ },
+ ) { _ -> }
+}
+
+@Preview
+@Composable
+private fun NetplayScreenPreview() {
+ NetplayScreen(
+ onBackClicked = {},
+ )
+}
\ No newline at end of file
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplaySetupActivity.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplaySetupActivity.kt
index 8e9cf05422..8058b3ad96 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplaySetupActivity.kt
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplaySetupActivity.kt
@@ -9,12 +9,12 @@ import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.collectAsState
+import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import org.dolphinemu.dolphinemu.activities.EmulationActivity
-import org.dolphinemu.dolphinemu.features.netplay.Netplay
import org.dolphinemu.dolphinemu.features.netplay.model.NetplaySetupViewModel
import org.dolphinemu.dolphinemu.ui.main.ThemeProvider
import org.dolphinemu.dolphinemu.ui.theme.DolphinTheme
@@ -28,14 +28,11 @@ class NetplaySetupActivity : AppCompatActivity(), ThemeProvider {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
- Netplay.launchGame
- .onEach { EmulationActivity.launch(this, it, false) }
- .launchIn(lifecycleScope)
-
val viewModel = ViewModelProvider(this)[NetplaySetupViewModel::class.java]
viewModel.showNetplayScreen
- .onEach { /* launch NetplayActivity */ }
+ .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
+ .onEach { NetplayActivity.launch(this) }
.launchIn(lifecycleScope)
setContent {
diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml
index 1944cc4911..903a414af3 100644
--- a/Source/Android/app/src/main/res/values/strings.xml
+++ b/Source/Android/app/src/main/res/values/strings.xml
@@ -995,4 +995,5 @@ It can efficiently compress both junk data and encrypted Wii data.
IP address
Host code
Port
+ Netplay