summaryrefslogtreecommitdiff
path: root/Source/Android
diff options
context:
space:
mode:
authorTom Pratt <tom.pratt@outlook.com>2026-04-06 13:32:57 +0100
committerTom Pratt <tompratt@squareup.com>2026-05-19 12:02:53 +0200
commitc716c32ce891b340f5b287f6ae589e43832c427e (patch)
tree4afefcaec758b59bb7c85574e4c1a92e4c5d691c /Source/Android
parent7b69949cec7afcd1691a9a046d26d5c4a99ce4fc (diff)
Add compose dependencies for Android and empty NetplaySetupActivity
Derive compose colour theming from the existing XML styles already set at the activity level.
Diffstat (limited to 'Source/Android')
-rw-r--r--Source/Android/app/build.gradle.kts11
-rw-r--r--Source/Android/app/src/main/AndroidManifest.xml5
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplaySetupActivity.kt107
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.kt6
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/theme/DolphinTheme.kt103
-rw-r--r--Source/Android/app/src/main/res/menu/menu_game_grid.xml5
-rw-r--r--Source/Android/app/src/main/res/values/strings.xml5
-rw-r--r--Source/Android/build.gradle.kts1
-rw-r--r--Source/Android/gradle/libs.versions.toml9
9 files changed, 252 insertions, 0 deletions
diff --git a/Source/Android/app/build.gradle.kts b/Source/Android/app/build.gradle.kts
index 0a952a0704..82196ff97b 100644
--- a/Source/Android/app/build.gradle.kts
+++ b/Source/Android/app/build.gradle.kts
@@ -1,5 +1,6 @@
plugins {
alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.compose)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.androidx.baselineprofile)
}
@@ -10,6 +11,7 @@ android {
ndkVersion = "29.0.14206865"
buildFeatures {
+ compose = true
viewBinding = true
buildConfig = true
resValues = true
@@ -150,6 +152,15 @@ dependencies {
implementation(libs.kotlinx.coroutines.android)
implementation(libs.filepicker)
+
+ // Jetpack Compose
+ implementation(platform(libs.androidx.compose.bom))
+ implementation(libs.androidx.activity.compose)
+ implementation(libs.androidx.compose.material.icons)
+ implementation(libs.androidx.compose.material3)
+ implementation(libs.androidx.compose.ui)
+ debugImplementation(libs.androidx.compose.ui.tooling)
+ debugImplementation(libs.androidx.compose.ui.tooling.preview)
}
fun getGitVersion(): String {
diff --git a/Source/Android/app/src/main/AndroidManifest.xml b/Source/Android/app/src/main/AndroidManifest.xml
index 3218ed3ed8..e68fa9f757 100644
--- a/Source/Android/app/src/main/AndroidManifest.xml
+++ b/Source/Android/app/src/main/AndroidManifest.xml
@@ -134,6 +134,11 @@
android:theme="@style/Theme.Dolphin.Main" />
<activity
+ android:name=".features.netplay.ui.NetplaySetupActivity"
+ android:exported="false"
+ android:theme="@style/Theme.Dolphin.Main" />
+
+ <activity
android:name=".features.riivolution.ui.RiivolutionBootActivity"
android:exported="false"
android:theme="@style/Theme.Dolphin.Main" />
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
new file mode 100644
index 0000000000..f79584c484
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplaySetupActivity.kt
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+@file:OptIn(ExperimentalMaterial3Api::class)
+
+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.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.consumeWindowInsets
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+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.MaterialTheme
+import androidx.compose.material3.MediumTopAppBar
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import org.dolphinemu.dolphinemu.R
+import org.dolphinemu.dolphinemu.ui.main.ThemeProvider
+import org.dolphinemu.dolphinemu.ui.theme.DolphinTheme
+import org.dolphinemu.dolphinemu.utils.ThemeHelper
+
+class NetplaySetupActivity : AppCompatActivity(), ThemeProvider {
+ override var themeId: Int = 0
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ ThemeHelper.setTheme(this)
+ enableEdgeToEdge()
+ super.onCreate(savedInstanceState)
+
+ setContent {
+ DolphinTheme {
+ NetplaySetupScreen(
+ 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, NetplaySetupActivity::class.java))
+ }
+ }
+}
+
+@Composable
+private fun NetplaySetupScreen(
+ onBackClicked: () -> Unit,
+) {
+ Scaffold(
+ topBar = {
+ MediumTopAppBar(
+ title = { Text(stringResource(R.string.netplay_setup_title)) },
+ navigationIcon = {
+ IconButton(onClick = onBackClicked) {
+ Icon(
+ Icons.AutoMirrored.Filled.ArrowBack,
+ contentDescription = "Back"
+ )
+ }
+ },
+ )
+ }
+ ) { innerPadding ->
+ Column(
+ modifier = Modifier
+ .consumeWindowInsets(innerPadding)
+ .padding(innerPadding)
+ .padding(horizontal = DolphinTheme.scaffoldPadding)
+ .verticalScroll(rememberScrollState()),
+ ) {
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun NetplaySetupScreenPreview() {
+ MaterialTheme {
+ NetplayScreen(onBackClicked = {})
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.kt
index 893d6a16d4..1e1d55dbd7 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.kt
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.kt
@@ -21,6 +21,7 @@ import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag
import org.dolphinemu.dolphinemu.features.sysupdate.ui.SystemMenuNotInstalledDialogFragment
import org.dolphinemu.dolphinemu.features.sysupdate.ui.SystemUpdateProgressBarDialogFragment
import org.dolphinemu.dolphinemu.features.sysupdate.ui.SystemUpdateViewModel
+import org.dolphinemu.dolphinemu.features.netplay.ui.NetplaySetupActivity
import org.dolphinemu.dolphinemu.fragments.AboutDialogFragment
import org.dolphinemu.dolphinemu.model.GameFileCache
import org.dolphinemu.dolphinemu.services.GameFileCacheManager
@@ -188,6 +189,11 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme
true
}
+ R.id.menu_netplay -> {
+ NetplaySetupActivity.launch(activity)
+ true
+ }
+
R.id.menu_about -> {
showAboutDialog()
false
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/theme/DolphinTheme.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/theme/DolphinTheme.kt
new file mode 100644
index 0000000000..58eb52b347
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/theme/DolphinTheme.kt
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.ui.theme
+
+import android.content.Context
+import androidx.annotation.AttrRes
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.ColorScheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.dp
+import com.google.android.material.color.MaterialColors
+import androidx.appcompat.R as AppCompatR
+import com.google.android.material.R as MaterialR
+
+object DolphinTheme {
+ val scaffoldPadding = 16.dp
+}
+
+@Composable
+fun DolphinTheme(content: @Composable () -> Unit) {
+ val context = LocalContext.current
+ val isDark = isSystemInDarkTheme()
+ val colorScheme = remember(context, isDark) { context.toDolphinColorScheme(isDark) }
+
+ MaterialTheme(
+ colorScheme = colorScheme,
+ content = content
+ )
+}
+
+private fun Context.toDolphinColorScheme(isDark: Boolean): ColorScheme {
+ fun attr(@AttrRes attr: Int) = Color(MaterialColors.getColor(this, attr, 0))
+
+ val background = obtainStyledAttributes(intArrayOf(android.R.attr.colorBackground)).use {
+ Color(it.getColor(0, 0))
+ }
+
+ return if (isDark) {
+ darkColorScheme(
+ primary = attr(AppCompatR.attr.colorPrimary),
+ onPrimary = attr(MaterialR.attr.colorOnPrimary),
+ primaryContainer = attr(MaterialR.attr.colorPrimaryContainer),
+ onPrimaryContainer = attr(MaterialR.attr.colorOnPrimaryContainer),
+ secondary = attr(MaterialR.attr.colorSecondary),
+ onSecondary = attr(MaterialR.attr.colorOnSecondary),
+ secondaryContainer = attr(MaterialR.attr.colorSecondaryContainer),
+ onSecondaryContainer = attr(MaterialR.attr.colorOnSecondaryContainer),
+ tertiary = attr(MaterialR.attr.colorTertiary),
+ onTertiary = attr(MaterialR.attr.colorOnTertiary),
+ tertiaryContainer = attr(MaterialR.attr.colorTertiaryContainer),
+ onTertiaryContainer = attr(MaterialR.attr.colorOnTertiaryContainer),
+ error = attr(AppCompatR.attr.colorError),
+ onError = attr(MaterialR.attr.colorOnError),
+ errorContainer = attr(MaterialR.attr.colorErrorContainer),
+ onErrorContainer = attr(MaterialR.attr.colorOnErrorContainer),
+ background = background,
+ onBackground = attr(MaterialR.attr.colorOnBackground),
+ surface = attr(MaterialR.attr.colorSurface),
+ onSurface = attr(MaterialR.attr.colorOnSurface),
+ surfaceVariant = attr(MaterialR.attr.colorSurfaceVariant),
+ onSurfaceVariant = attr(MaterialR.attr.colorOnSurfaceVariant),
+ outline = attr(MaterialR.attr.colorOutline),
+ inverseSurface = attr(MaterialR.attr.colorSurfaceInverse),
+ inverseOnSurface = attr(MaterialR.attr.colorOnSurfaceInverse),
+ inversePrimary = attr(MaterialR.attr.colorPrimaryInverse),
+ )
+ } else {
+ lightColorScheme(
+ primary = attr(AppCompatR.attr.colorPrimary),
+ onPrimary = attr(MaterialR.attr.colorOnPrimary),
+ primaryContainer = attr(MaterialR.attr.colorPrimaryContainer),
+ onPrimaryContainer = attr(MaterialR.attr.colorOnPrimaryContainer),
+ secondary = attr(MaterialR.attr.colorSecondary),
+ onSecondary = attr(MaterialR.attr.colorOnSecondary),
+ secondaryContainer = attr(MaterialR.attr.colorSecondaryContainer),
+ onSecondaryContainer = attr(MaterialR.attr.colorOnSecondaryContainer),
+ tertiary = attr(MaterialR.attr.colorTertiary),
+ onTertiary = attr(MaterialR.attr.colorOnTertiary),
+ tertiaryContainer = attr(MaterialR.attr.colorTertiaryContainer),
+ onTertiaryContainer = attr(MaterialR.attr.colorOnTertiaryContainer),
+ error = attr(AppCompatR.attr.colorError),
+ onError = attr(MaterialR.attr.colorOnError),
+ errorContainer = attr(MaterialR.attr.colorErrorContainer),
+ onErrorContainer = attr(MaterialR.attr.colorOnErrorContainer),
+ background = background,
+ onBackground = attr(MaterialR.attr.colorOnBackground),
+ surface = attr(MaterialR.attr.colorSurface),
+ onSurface = attr(MaterialR.attr.colorOnSurface),
+ surfaceVariant = attr(MaterialR.attr.colorSurfaceVariant),
+ onSurfaceVariant = attr(MaterialR.attr.colorOnSurfaceVariant),
+ outline = attr(MaterialR.attr.colorOutline),
+ inverseSurface = attr(MaterialR.attr.colorSurfaceInverse),
+ inverseOnSurface = attr(MaterialR.attr.colorOnSurfaceInverse),
+ inversePrimary = attr(MaterialR.attr.colorPrimaryInverse),
+ )
+ }
+}
diff --git a/Source/Android/app/src/main/res/menu/menu_game_grid.xml b/Source/Android/app/src/main/res/menu/menu_game_grid.xml
index 2a282198d1..c52c574c44 100644
--- a/Source/Android/app/src/main/res/menu/menu_game_grid.xml
+++ b/Source/Android/app/src/main/res/menu/menu_game_grid.xml
@@ -52,6 +52,11 @@
app:showAsAction="never"/>
<item
+ android:id="@+id/menu_netplay"
+ android:title="@string/grid_menu_netplay"
+ app:showAsAction="never"/>
+
+ <item
android:id="@+id/menu_about"
android:title="@string/grid_menu_about"
app:showAsAction="never"/>
diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml
index 04c48bcba4..712d4744cc 100644
--- a/Source/Android/app/src/main/res/values/strings.xml
+++ b/Source/Android/app/src/main/res/values/strings.xml
@@ -495,6 +495,7 @@
<string name="grid_menu_import_wii_save">Import Wii Save</string>
<string name="grid_menu_import_nand_backup">Import BootMii NAND Backup</string>
<string name="grid_menu_online_system_update">Perform Online System Update</string>
+ <string name="grid_menu_netplay">Netplay</string>
<string name="grid_menu_load_wii_system_menu">Load Wii System Menu</string>
<string name="grid_menu_load_wii_system_menu_installed">Load Wii System Menu (%s)</string>
<string name="grid_menu_load_vwii_system_menu_installed">Load vWii System Menu (%s)</string>
@@ -980,4 +981,8 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="achievements_logout">Log Out</string>
<string name="achievements_login_in_progress">Logging In</string>
<string name="achievements_login_failed">Login Failed</string>
+
+ <!-- Netplay -->
+ <string name="netplay_setup_title">Netplay Setup</string>
+ <string name="netplay_nickname_label">Nickname</string>
</resources>
diff --git a/Source/Android/build.gradle.kts b/Source/Android/build.gradle.kts
index 5682b2f130..c74d069b44 100644
--- a/Source/Android/build.gradle.kts
+++ b/Source/Android/build.gradle.kts
@@ -4,6 +4,7 @@ plugins {
alias(libs.plugins.android.library) apply false
alias(libs.plugins.android.test) apply false
alias(libs.plugins.androidx.baselineprofile) apply false
+ alias(libs.plugins.kotlin.compose) apply false
}
buildscript {
diff --git a/Source/Android/gradle/libs.versions.toml b/Source/Android/gradle/libs.versions.toml
index 09fa7b09ca..e29e0c2e10 100644
--- a/Source/Android/gradle/libs.versions.toml
+++ b/Source/Android/gradle/libs.versions.toml
@@ -4,6 +4,7 @@ appcompat = "1.7.1"
benchmarkMacroJunit4 = "1.5.0-alpha04"
cardview = "1.0.0"
coil = "2.7.0"
+compose-bom = "2025.04.00"
constraintlayout = "2.2.1"
coreKtx = "1.18.0"
coreSplashscreen = "1.2.0"
@@ -27,9 +28,16 @@ tvprovider = "1.1.0"
uiautomator = "2.3.0"
[libraries]
+androidx-activity-compose = { group = "androidx.activity", name = "activity-compose" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
androidx-benchmark-macro-junit4 = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "benchmarkMacroJunit4" }
androidx-cardview = { group = "androidx.cardview", name = "cardview", version.ref = "cardview" }
+androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
+androidx-compose-material-icons = { group = "androidx.compose.material", name = "material-icons-extended" }
+androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
+androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
+androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
+androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "coreSplashscreen" }
@@ -58,4 +66,5 @@ android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
android-test = { id = "com.android.test", version.ref = "agp" }
androidx-baselineprofile = { id = "androidx.baselineprofile", version.ref = "benchmarkMacroJunit4" }
+kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }