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

View File

@ -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"

View File

@ -134,6 +134,10 @@ object Netplay {
false
}
suspend fun quit() = withContext(Dispatchers.IO) {
releaseNetplayClient()
}
private fun releaseNetplayClient() {
if (netPlayClientPointer != 0L) {
ReleaseNetplayClient()

View File

@ -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()
}
}
}

View File

@ -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))
}
}
}

View File

@ -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 = {},
)
}

View File

@ -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 {

View File

@ -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>