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 @@ -133,6 +133,11 @@ android:label="@string/user_data_submenu" android:theme="@style/Theme.Dolphin.Main" /> + + 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 @@ -51,6 +51,11 @@ android:title="@string/grid_menu_online_system_update" app:showAsAction="never"/> + + Import Wii Save Import BootMii NAND Backup Perform Online System Update + Netplay Load Wii System Menu Load Wii System Menu (%s) Load vWii System Menu (%s) @@ -980,4 +981,8 @@ It can efficiently compress both junk data and encrypted Wii data. Log Out Logging In Login Failed + + + Netplay Setup + Nickname 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" }