forked from MeloNX/MeloNX
add settings view
This commit is contained in:
parent
21a78f173e
commit
7b1ce437ef
@ -10,6 +10,7 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.ryujinx.android.viewmodels.GameModel
|
import org.ryujinx.android.viewmodels.GameModel
|
||||||
import org.ryujinx.android.viewmodels.MainViewModel
|
import org.ryujinx.android.viewmodels.MainViewModel
|
||||||
|
import org.ryujinx.android.viewmodels.QuickSettings
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@ -105,16 +106,22 @@ class GameHost(context: Context?, val controller: GameController, val mainViewMo
|
|||||||
success = _nativeRyujinx.graphicsInitializeRenderer(
|
success = _nativeRyujinx.graphicsInitializeRenderer(
|
||||||
nativeInterop!!.VkRequiredExtensions!!,
|
nativeInterop!!.VkRequiredExtensions!!,
|
||||||
window
|
window
|
||||||
|
)
|
||||||
|
|
||||||
|
var settings = QuickSettings(mainViewModel.activity)
|
||||||
|
|
||||||
|
success = _nativeRyujinx.deviceInitialize(
|
||||||
|
settings.isHostMapped,
|
||||||
|
settings.useNce,
|
||||||
|
SystemLanguage.AmericanEnglish.ordinal,
|
||||||
|
RegionCode.USA.ordinal,
|
||||||
|
settings.enableVsync,
|
||||||
|
settings.enableDocked,
|
||||||
|
settings.enablePtc,
|
||||||
|
false,
|
||||||
|
"UTC",
|
||||||
|
settings.ignoreMissingServices
|
||||||
);
|
);
|
||||||
success = _nativeRyujinx.deviceInitialize(true, true,
|
|
||||||
SystemLanguage.AmericanEnglish.ordinal,
|
|
||||||
RegionCode.USA.ordinal,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
"UTC",
|
|
||||||
false);
|
|
||||||
|
|
||||||
success = _nativeRyujinx.deviceLoad(path)
|
success = _nativeRyujinx.deviceLoad(path)
|
||||||
|
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
package org.ryujinx.android.viewmodels
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import org.ryujinx.android.MainActivity
|
||||||
|
|
||||||
|
class QuickSettings(val activity: MainActivity) {
|
||||||
|
var ignoreMissingServices: Boolean
|
||||||
|
var enablePtc: Boolean
|
||||||
|
var enableDocked: Boolean
|
||||||
|
var enableVsync: Boolean
|
||||||
|
var useNce: Boolean
|
||||||
|
var isHostMapped: Boolean
|
||||||
|
|
||||||
|
private var sharedPref: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
|
|
||||||
|
init {
|
||||||
|
isHostMapped = sharedPref.getBoolean("isHostMapped", true)
|
||||||
|
useNce = sharedPref.getBoolean("useNce", true)
|
||||||
|
enableVsync = sharedPref.getBoolean("enableVsync", true)
|
||||||
|
enableDocked = sharedPref.getBoolean("enableDocked", true)
|
||||||
|
enablePtc = sharedPref.getBoolean("enablePtc", true)
|
||||||
|
ignoreMissingServices = sharedPref.getBoolean("ignoreMissingServices", false)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package org.ryujinx.android.viewmodels
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import org.ryujinx.android.MainActivity
|
||||||
|
|
||||||
|
class SettingsViewModel(var navController: NavHostController, val activity: MainActivity) {
|
||||||
|
private var sharedPref: SharedPreferences
|
||||||
|
|
||||||
|
init {
|
||||||
|
sharedPref = getPreferences()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPreferences() : SharedPreferences {
|
||||||
|
return PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun initializeState(isHostMapped : MutableState<Boolean>,
|
||||||
|
useNce : MutableState<Boolean>,
|
||||||
|
enableVsync : MutableState<Boolean>,
|
||||||
|
enableDocked : MutableState<Boolean>,
|
||||||
|
enablePtc : MutableState<Boolean>,
|
||||||
|
ignoreMissingServices : MutableState<Boolean>)
|
||||||
|
{
|
||||||
|
|
||||||
|
isHostMapped.value = sharedPref.getBoolean("isHostMapped", true)
|
||||||
|
useNce.value = sharedPref.getBoolean("useNce", true)
|
||||||
|
enableVsync.value = sharedPref.getBoolean("enableVsync", true)
|
||||||
|
enableDocked.value = sharedPref.getBoolean("enableDocked", true)
|
||||||
|
enablePtc.value = sharedPref.getBoolean("enablePtc", true)
|
||||||
|
ignoreMissingServices.value = sharedPref.getBoolean("ignoreMissingServices", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun save(isHostMapped : MutableState<Boolean>,
|
||||||
|
useNce : MutableState<Boolean>,
|
||||||
|
enableVsync : MutableState<Boolean>,
|
||||||
|
enableDocked : MutableState<Boolean>,
|
||||||
|
enablePtc : MutableState<Boolean>,
|
||||||
|
ignoreMissingServices : MutableState<Boolean>){
|
||||||
|
var editor = sharedPref.edit()
|
||||||
|
|
||||||
|
editor.putBoolean("isHostMapped", isHostMapped?.value ?: true)
|
||||||
|
editor.putBoolean("useNce", useNce?.value ?: true)
|
||||||
|
editor.putBoolean("enableVsync", enableVsync?.value ?: true)
|
||||||
|
editor.putBoolean("enableDocked", enableDocked?.value ?: true)
|
||||||
|
editor.putBoolean("enablePtc", enablePtc?.value ?: true)
|
||||||
|
editor.putBoolean("ignoreMissingServices", ignoreMissingServices?.value ?: false)
|
||||||
|
|
||||||
|
editor.apply()
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package org.ryujinx.android.views
|
package org.ryujinx.android.views
|
||||||
|
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
|
import android.view.Gravity
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@ -21,6 +22,7 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material.icons.filled.MoreVert
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.AlertDialogDefaults
|
import androidx.compose.material3.AlertDialogDefaults
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
@ -36,6 +38,7 @@ import androidx.compose.material3.Scaffold
|
|||||||
import androidx.compose.material3.SearchBarDefaults
|
import androidx.compose.material3.SearchBarDefaults
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.material3.rememberModalBottomSheetState
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@ -47,10 +50,17 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.layout.onSizeChanged
|
||||||
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.IntOffset
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.DialogWindowProvider
|
||||||
|
import androidx.compose.ui.window.Popup
|
||||||
import androidx.compose.ui.zIndex
|
import androidx.compose.ui.zIndex
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import org.ryujinx.android.MainActivity
|
import org.ryujinx.android.MainActivity
|
||||||
import org.ryujinx.android.R
|
import org.ryujinx.android.R
|
||||||
@ -65,48 +75,98 @@ class HomeViews {
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MainTopBar() {
|
fun MainTopBar(navController: NavHostController) {
|
||||||
TopAppBar(
|
var topBarSize = remember {
|
||||||
modifier = Modifier
|
mutableStateOf(0)
|
||||||
.zIndex(1f)
|
}
|
||||||
.padding(top = 10.dp),
|
Column {
|
||||||
title = {
|
var showOptionsPopup = remember {
|
||||||
DockedSearchBar(
|
mutableStateOf(false)
|
||||||
shape = SearchBarDefaults.inputFieldShape,
|
}
|
||||||
query = "",
|
TopAppBar(
|
||||||
onQueryChange = {},
|
modifier = Modifier
|
||||||
onSearch = {},
|
.zIndex(1f)
|
||||||
active = false,
|
.padding(top = 16.dp)
|
||||||
onActiveChange = {},
|
.onSizeChanged {
|
||||||
leadingIcon = {
|
topBarSize.value = it.height
|
||||||
Icon(
|
|
||||||
Icons.Filled.Search,
|
|
||||||
contentDescription = "Search Games"
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
placeholder = {
|
title = {
|
||||||
Text(text = "Search Games")
|
DockedSearchBar(
|
||||||
}
|
shape = SearchBarDefaults.inputFieldShape,
|
||||||
) {
|
query = "",
|
||||||
|
onQueryChange = {},
|
||||||
|
onSearch = {},
|
||||||
|
active = false,
|
||||||
|
onActiveChange = {},
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
Icons.Filled.Search,
|
||||||
|
contentDescription = "Search Games"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
placeholder = {
|
||||||
|
Text(text = "Search Games")
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions = {
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
showOptionsPopup.value = true;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Filled.MoreVert,
|
||||||
|
contentDescription = "More"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
)
|
||||||
actions = {
|
Box {
|
||||||
IconButton(
|
if(showOptionsPopup.value)
|
||||||
onClick = { }
|
{
|
||||||
) {
|
AlertDialog(
|
||||||
Icon(
|
modifier = Modifier.padding(top = (topBarSize.value / Resources.getSystem().displayMetrics.density + 10).dp,
|
||||||
Icons.Filled.MoreVert,
|
start = 16.dp, end = 16.dp),
|
||||||
contentDescription = "More"
|
onDismissRequest = {
|
||||||
)
|
showOptionsPopup.value = false
|
||||||
|
}) {
|
||||||
|
val dialogWindowProvider = LocalView.current.parent as DialogWindowProvider
|
||||||
|
dialogWindowProvider.window.setGravity(Gravity.TOP)
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.wrapContentHeight()
|
||||||
|
.padding(16.dp),
|
||||||
|
shape = MaterialTheme.shapes.large,
|
||||||
|
tonalElevation = AlertDialogDefaults.TonalElevation
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
TextButton(onClick = {
|
||||||
|
navController.navigate("settings")
|
||||||
|
}, modifier = Modifier.fillMaxWidth()
|
||||||
|
.align(Alignment.Start),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Filled.Settings,
|
||||||
|
contentDescription = "Settings"
|
||||||
|
)
|
||||||
|
Text(text = "Settings", modifier = Modifier.padding(16.dp)
|
||||||
|
.align(Alignment.CenterVertically))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun Home(viewModel: HomeViewModel = HomeViewModel()) {
|
fun Home(viewModel: HomeViewModel = HomeViewModel(), navController: NavHostController? = null) {
|
||||||
val sheetState = rememberModalBottomSheetState()
|
val sheetState = rememberModalBottomSheetState()
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
var showBottomSheet = remember { mutableStateOf(false) }
|
var showBottomSheet = remember { mutableStateOf(false) }
|
||||||
@ -114,7 +174,9 @@ class HomeViews {
|
|||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
topBar = {
|
topBar = {
|
||||||
MainTopBar()
|
navController?.apply {
|
||||||
|
MainTopBar(navController)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
floatingActionButtonPosition = FabPosition.End,
|
floatingActionButtonPosition = FabPosition.End,
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
@ -168,7 +230,7 @@ class HomeViews {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Surface(color = MaterialTheme.colorScheme.surface,
|
Surface(color = MaterialTheme.colorScheme.surface,
|
||||||
modifier = Modifier.padding(10.dp)) {
|
modifier = Modifier.padding(16.dp)) {
|
||||||
Column(modifier = Modifier.fillMaxSize()) {
|
Column(modifier = Modifier.fillMaxSize()) {
|
||||||
Row(modifier = Modifier.align(Alignment.CenterHorizontally)) {
|
Row(modifier = Modifier.align(Alignment.CenterHorizontally)) {
|
||||||
Card(
|
Card(
|
||||||
@ -176,7 +238,7 @@ class HomeViews {
|
|||||||
openDialog.value = true
|
openDialog.value = true
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.padding(10.dp)) {
|
Column(modifier = Modifier.padding(16.dp)) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(R.drawable.app_update),
|
painter = painterResource(R.drawable.app_update),
|
||||||
contentDescription = "More",
|
contentDescription = "More",
|
||||||
@ -206,7 +268,7 @@ class HomeViews {
|
|||||||
Card(shape = MaterialTheme.shapes.medium,
|
Card(shape = MaterialTheme.shapes.medium,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(10.dp)
|
.padding(16.dp)
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
if (gameModel.titleId.isNullOrEmpty() || gameModel.titleId != "0000000000000000") {
|
if (gameModel.titleId.isNullOrEmpty() || gameModel.titleId != "0000000000000000") {
|
||||||
@ -219,7 +281,7 @@ class HomeViews {
|
|||||||
})) {
|
})) {
|
||||||
Row(modifier = Modifier
|
Row(modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(10.dp),
|
.padding(16.dp),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween) {
|
horizontalArrangement = Arrangement.SpaceBetween) {
|
||||||
Row {
|
Row {
|
||||||
if(!gameModel.titleId.isNullOrEmpty() && gameModel.titleId != "0000000000000000")
|
if(!gameModel.titleId.isNullOrEmpty() && gameModel.titleId != "0000000000000000")
|
||||||
@ -231,7 +293,7 @@ class HomeViews {
|
|||||||
AsyncImage(model = imageFile,
|
AsyncImage(model = imageFile,
|
||||||
contentDescription = gameModel.titleName + " icon",
|
contentDescription = gameModel.titleName + " icon",
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(end = 5.dp)
|
.padding(end = 8.dp)
|
||||||
.width(size.roundToInt().dp)
|
.width(size.roundToInt().dp)
|
||||||
.height(size.roundToInt().dp))
|
.height(size.roundToInt().dp))
|
||||||
}
|
}
|
||||||
@ -258,7 +320,7 @@ class HomeViews {
|
|||||||
Icons.Filled.Add,
|
Icons.Filled.Add,
|
||||||
contentDescription = "Options",
|
contentDescription = "Options",
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(end = 5.dp)
|
.padding(end = 8.dp)
|
||||||
.width(size.roundToInt().dp)
|
.width(size.roundToInt().dp)
|
||||||
.height(size.roundToInt().dp)
|
.height(size.roundToInt().dp)
|
||||||
)
|
)
|
||||||
|
@ -20,6 +20,7 @@ import androidx.navigation.compose.rememberNavController
|
|||||||
import org.ryujinx.android.GameController
|
import org.ryujinx.android.GameController
|
||||||
import org.ryujinx.android.GameHost
|
import org.ryujinx.android.GameHost
|
||||||
import org.ryujinx.android.viewmodels.MainViewModel
|
import org.ryujinx.android.viewmodels.MainViewModel
|
||||||
|
import org.ryujinx.android.viewmodels.SettingsViewModel
|
||||||
|
|
||||||
class MainView {
|
class MainView {
|
||||||
companion object {
|
companion object {
|
||||||
@ -29,8 +30,9 @@ class MainView {
|
|||||||
mainViewModel.setNavController(navController)
|
mainViewModel.setNavController(navController)
|
||||||
|
|
||||||
NavHost(navController = navController, startDestination = "home") {
|
NavHost(navController = navController, startDestination = "home") {
|
||||||
composable("home") {HomeViews.Home(mainViewModel.homeViewModel)}
|
composable("home") { HomeViews.Home(mainViewModel.homeViewModel, navController) }
|
||||||
composable("game") { GameView(mainViewModel)}
|
composable("game") { GameView(mainViewModel) }
|
||||||
|
composable("settings") { SettingViews.Main(SettingsViewModel(navController, mainViewModel.activity)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +63,7 @@ class MainView {
|
|||||||
mutableStateOf(0.0)
|
mutableStateOf(0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
Surface(modifier = Modifier.padding(10.dp),
|
Surface(modifier = Modifier.padding(16.dp),
|
||||||
color = MaterialTheme.colorScheme.surface.copy(0.4f)) {
|
color = MaterialTheme.colorScheme.surface.copy(0.4f)) {
|
||||||
Column {
|
Column {
|
||||||
var gameTimeVal = 0.0;
|
var gameTimeVal = 0.0;
|
||||||
|
@ -0,0 +1,346 @@
|
|||||||
|
package org.ryujinx.android.views
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.animateColor
|
||||||
|
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||||
|
import androidx.compose.animation.core.MutableTransitionState
|
||||||
|
import androidx.compose.animation.core.animateDp
|
||||||
|
import androidx.compose.animation.core.animateFloat
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.animation.core.updateTransition
|
||||||
|
import androidx.compose.animation.expandVertically
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.shrinkVertically
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Switch
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.rotate
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import org.ryujinx.android.viewmodels.SettingsViewModel
|
||||||
|
|
||||||
|
class SettingViews {
|
||||||
|
companion object {
|
||||||
|
const val EXPANSTION_TRANSITION_DURATION = 450
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun Main(settingsViewModel: SettingsViewModel) {
|
||||||
|
var loaded = remember {
|
||||||
|
mutableStateOf(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
var isHostMapped = remember {
|
||||||
|
mutableStateOf(false)
|
||||||
|
}
|
||||||
|
var useNce = remember {
|
||||||
|
mutableStateOf(false)
|
||||||
|
}
|
||||||
|
var enableVsync = remember {
|
||||||
|
mutableStateOf(false)
|
||||||
|
}
|
||||||
|
var enableDocked = remember {
|
||||||
|
mutableStateOf(false)
|
||||||
|
}
|
||||||
|
var enablePtc = remember {
|
||||||
|
mutableStateOf(false)
|
||||||
|
}
|
||||||
|
var ignoreMissingServices = remember {
|
||||||
|
mutableStateOf(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loaded.value) {
|
||||||
|
settingsViewModel.initializeState(
|
||||||
|
isHostMapped,
|
||||||
|
useNce,
|
||||||
|
enableVsync, enableDocked, enablePtc, ignoreMissingServices
|
||||||
|
)
|
||||||
|
loaded.value = true
|
||||||
|
}
|
||||||
|
Scaffold(modifier = Modifier.fillMaxSize(),
|
||||||
|
topBar = {
|
||||||
|
TopAppBar(title = {
|
||||||
|
Text(text = "Settings")
|
||||||
|
},
|
||||||
|
modifier = Modifier.padding(top = 16.dp),
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(onClick = {
|
||||||
|
settingsViewModel.save(
|
||||||
|
isHostMapped,
|
||||||
|
useNce,
|
||||||
|
enableVsync,
|
||||||
|
enableDocked,
|
||||||
|
enablePtc,
|
||||||
|
ignoreMissingServices
|
||||||
|
)
|
||||||
|
settingsViewModel.navController.popBackStack()
|
||||||
|
}) {
|
||||||
|
Icon(Icons.Filled.ArrowBack, contentDescription = "Back")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}) { contentPadding ->
|
||||||
|
Column(modifier = Modifier.padding(contentPadding)) {
|
||||||
|
BackHandler {
|
||||||
|
settingsViewModel.save(
|
||||||
|
isHostMapped,
|
||||||
|
useNce, enableVsync, enableDocked, enablePtc, ignoreMissingServices
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ExpandableView(onCardArrowClick = { }, title = "System") {
|
||||||
|
Column(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(8.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Use NCE",
|
||||||
|
modifier = Modifier.align(Alignment.CenterVertically)
|
||||||
|
)
|
||||||
|
Switch(checked = useNce.value, onCheckedChange = {
|
||||||
|
useNce.value = !useNce.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(8.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Is Host Mapped",
|
||||||
|
modifier = Modifier.align(Alignment.CenterVertically)
|
||||||
|
)
|
||||||
|
Switch(checked = isHostMapped.value, onCheckedChange = {
|
||||||
|
isHostMapped.value = !isHostMapped.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(8.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Enable VSync",
|
||||||
|
modifier = Modifier.align(Alignment.CenterVertically)
|
||||||
|
)
|
||||||
|
Switch(checked = enableVsync.value, onCheckedChange = {
|
||||||
|
enableVsync.value = !enableVsync.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(8.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Enable PTC",
|
||||||
|
modifier = Modifier.align(Alignment.CenterVertically)
|
||||||
|
)
|
||||||
|
Switch(checked = enablePtc.value, onCheckedChange = {
|
||||||
|
enablePtc.value = !enablePtc.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(8.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Enable Docked Mode",
|
||||||
|
modifier = Modifier.align(Alignment.CenterVertically)
|
||||||
|
)
|
||||||
|
Switch(checked = enableDocked.value, onCheckedChange = {
|
||||||
|
enableDocked.value = !enableDocked.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(8.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Ignore Missing Services",
|
||||||
|
modifier = Modifier.align(Alignment.CenterVertically)
|
||||||
|
)
|
||||||
|
Switch(checked = ignoreMissingServices.value, onCheckedChange = {
|
||||||
|
ignoreMissingServices.value = !ignoreMissingServices.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
@SuppressLint("UnusedTransitionTargetStateParameter")
|
||||||
|
fun ExpandableView(
|
||||||
|
onCardArrowClick: () -> Unit,
|
||||||
|
title: String,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
var expanded = false
|
||||||
|
var mutableExpanded = remember {
|
||||||
|
mutableStateOf(expanded)
|
||||||
|
}
|
||||||
|
val transitionState = remember {
|
||||||
|
MutableTransitionState(expanded).apply {
|
||||||
|
targetState = !mutableExpanded.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val transition = updateTransition(transitionState, label = "transition")
|
||||||
|
val cardPaddingHorizontal by transition.animateDp({
|
||||||
|
tween(durationMillis = EXPANSTION_TRANSITION_DURATION)
|
||||||
|
}, label = "paddingTransition") {
|
||||||
|
if (mutableExpanded.value) 48.dp else 24.dp
|
||||||
|
}
|
||||||
|
val cardElevation by transition.animateDp({
|
||||||
|
tween(durationMillis = EXPANSTION_TRANSITION_DURATION)
|
||||||
|
}, label = "elevationTransition") {
|
||||||
|
if (mutableExpanded.value) 24.dp else 4.dp
|
||||||
|
}
|
||||||
|
val cardRoundedCorners by transition.animateDp({
|
||||||
|
tween(
|
||||||
|
durationMillis = EXPANSTION_TRANSITION_DURATION,
|
||||||
|
easing = FastOutSlowInEasing
|
||||||
|
)
|
||||||
|
}, label = "cornersTransition") {
|
||||||
|
if (mutableExpanded.value) 0.dp else 16.dp
|
||||||
|
}
|
||||||
|
val arrowRotationDegree by transition.animateFloat({
|
||||||
|
tween(durationMillis = EXPANSTION_TRANSITION_DURATION)
|
||||||
|
}, label = "rotationDegreeTransition") {
|
||||||
|
if (mutableExpanded.value) 0f else 180f
|
||||||
|
}
|
||||||
|
|
||||||
|
Card(
|
||||||
|
shape = MaterialTheme.shapes.medium,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(
|
||||||
|
horizontal = cardPaddingHorizontal,
|
||||||
|
vertical = 8.dp
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
Card(
|
||||||
|
onClick = {
|
||||||
|
mutableExpanded.value = !mutableExpanded.value
|
||||||
|
onCardArrowClick()
|
||||||
|
}) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
CardTitle(title = title)
|
||||||
|
CardArrow(
|
||||||
|
degrees = arrowRotationDegree,
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExpandableContent(visible = mutableExpanded.value, content = content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CardArrow(
|
||||||
|
degrees: Float,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Filled.KeyboardArrowUp,
|
||||||
|
contentDescription = "Expandable Arrow",
|
||||||
|
modifier = Modifier.padding(8.dp).rotate(degrees),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CardTitle(title: String) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ExpandableContent(
|
||||||
|
visible: Boolean = true,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
val enterTransition = remember {
|
||||||
|
expandVertically(
|
||||||
|
expandFrom = Alignment.Top,
|
||||||
|
animationSpec = tween(EXPANSTION_TRANSITION_DURATION)
|
||||||
|
) + fadeIn(
|
||||||
|
initialAlpha = 0.3f,
|
||||||
|
animationSpec = tween(EXPANSTION_TRANSITION_DURATION)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val exitTransition = remember {
|
||||||
|
shrinkVertically(
|
||||||
|
// Expand from the top.
|
||||||
|
shrinkTowards = Alignment.Top,
|
||||||
|
animationSpec = tween(EXPANSTION_TRANSITION_DURATION)
|
||||||
|
) + fadeOut(
|
||||||
|
// Fade in with the initial alpha of 0.3f.
|
||||||
|
animationSpec = tween(EXPANSTION_TRANSITION_DURATION)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = visible,
|
||||||
|
enter = enterTransition,
|
||||||
|
exit = exitTransition
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.padding(8.dp)) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -46,7 +46,7 @@ class TitleUpdateViews {
|
|||||||
Text(text = "Updates for ${name}", textAlign = TextAlign.Center)
|
Text(text = "Updates for ${name}", textAlign = TextAlign.Center)
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(5.dp),
|
.padding(8.dp),
|
||||||
color = MaterialTheme.colorScheme.surfaceVariant,
|
color = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
shape = MaterialTheme.shapes.medium
|
shape = MaterialTheme.shapes.medium
|
||||||
) {
|
) {
|
||||||
@ -55,7 +55,7 @@ class TitleUpdateViews {
|
|||||||
.height(300.dp)
|
.height(300.dp)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Row(modifier = Modifier.padding(5.dp)) {
|
Row(modifier = Modifier.padding(8.dp)) {
|
||||||
RadioButton(
|
RadioButton(
|
||||||
selected = (selected.value == 0),
|
selected = (selected.value == 0),
|
||||||
onClick = { selected.value = 0
|
onClick = { selected.value = 0
|
||||||
@ -75,7 +75,7 @@ class TitleUpdateViews {
|
|||||||
var index = 1
|
var index = 1
|
||||||
for (path in paths) {
|
for (path in paths) {
|
||||||
var i = index
|
var i = index
|
||||||
Row(modifier = Modifier.padding(5.dp)) {
|
Row(modifier = Modifier.padding(8.dp)) {
|
||||||
RadioButton(
|
RadioButton(
|
||||||
selected = (selected.value == i),
|
selected = (selected.value == i),
|
||||||
onClick = { selected.value = i })
|
onClick = { selected.value = i })
|
||||||
@ -115,7 +115,7 @@ class TitleUpdateViews {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(15.dp))
|
Spacer(modifier = Modifier.height(18.dp))
|
||||||
TextButton(
|
TextButton(
|
||||||
modifier = Modifier.align(Alignment.End),
|
modifier = Modifier.align(Alignment.End),
|
||||||
onClick = {
|
onClick = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user