Add mostly empty Netplay screen, equivalent of NetPlayDialog in QT.

All it can do at this point is quit the current netplay session when backing out of this screen.
This commit is contained in:
Tom Pratt
2026-04-09 13:43:57 +01:00
committed by Tom Pratt
parent 5527e8138e
commit b72f3c1afc
7 changed files with 161 additions and 7 deletions
@@ -138,6 +138,11 @@
android:exported="false"
android:theme="@style/Theme.Dolphin.Main" />
<activity
android:name=".features.netplay.ui.NetplayActivity"
android:exported="false"
android:theme="@style/Theme.Dolphin.Main" />
<activity
android:name=".features.riivolution.ui.RiivolutionBootActivity"
android:exported="false"
@@ -134,6 +134,10 @@ object Netplay {
false
}
suspend fun quit() = withContext(Dispatchers.IO) {
releaseNetplayClient()
}
private fun releaseNetplayClient() {
if (netPlayClientPointer != 0L) {
ReleaseNetplayClient()
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.netplay.model
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.Channel.Factory.CONFLATED
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import org.dolphinemu.dolphinemu.features.netplay.Netplay
class NetplayViewModel : ViewModel() {
val launchGame = Netplay.launchGame
private val _goBack = Channel<Unit>(CONFLATED)
val goBack = _goBack.receiveAsFlow()
init {
if (!Netplay.isClientConnected()) {
_goBack.trySend(Unit)
}
}
@OptIn(DelicateCoroutinesApi::class)
override fun onCleared() {
super.onCleared()
GlobalScope.launch {
Netplay.quit()
}
}
}
@@ -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))
}
}
}
@@ -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 = {},
)
}
@@ -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 {
@@ -995,4 +995,5 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="netplay_ip_address_label">IP address</string>
<string name="netplay_host_code_label">Host code</string>
<string name="netplay_port_label">Port</string>
<string name="netplay_title">Netplay</string>
</resources>