forked from MeloNX/MeloNX
android - update version
android - stop loading game if update fails android - add refresh list button android - fix update error return code android - fix compose error with game list android - add progress indicator for game list android - fix game list loading android - adjust virtual controller button positions. added stick sensitivity option android - bump version android - fix vulkan driver install
This commit is contained in:
parent
d19582b055
commit
1a94e37816
@ -11,8 +11,8 @@ android {
|
|||||||
applicationId "org.ryujinx.android"
|
applicationId "org.ryujinx.android"
|
||||||
minSdk 30
|
minSdk 30
|
||||||
targetSdk 34
|
targetSdk 34
|
||||||
versionCode 10032
|
versionCode 10039
|
||||||
versionName '1.0.32'
|
versionName '1.0.39'
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
|
@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import androidx.core.math.MathUtils
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.swordfish.radialgamepad.library.RadialGamePad
|
import com.swordfish.radialgamepad.library.RadialGamePad
|
||||||
@ -193,19 +194,25 @@ class GameController(var activity: Activity) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
GamePadButtonInputId.LeftStick.ordinal -> {
|
GamePadButtonInputId.LeftStick.ordinal -> {
|
||||||
|
val setting = QuickSettings(activity)
|
||||||
|
val x = MathUtils.clamp(ev.xAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
val y = MathUtils.clamp(ev.yAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
RyujinxNative.jnaInstance.inputSetStickAxis(
|
RyujinxNative.jnaInstance.inputSetStickAxis(
|
||||||
1,
|
1,
|
||||||
ev.xAxis,
|
x,
|
||||||
-ev.yAxis,
|
-y,
|
||||||
this
|
this
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
GamePadButtonInputId.RightStick.ordinal -> {
|
GamePadButtonInputId.RightStick.ordinal -> {
|
||||||
|
val setting = QuickSettings(activity)
|
||||||
|
val x = MathUtils.clamp(ev.xAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
|
val y = MathUtils.clamp(ev.yAxis * setting.controllerStickSensitivity, -1f, 1f)
|
||||||
RyujinxNative.jnaInstance.inputSetStickAxis(
|
RyujinxNative.jnaInstance.inputSetStickAxis(
|
||||||
2,
|
2,
|
||||||
ev.xAxis,
|
x,
|
||||||
-ev.yAxis,
|
-y,
|
||||||
this
|
this
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -226,7 +233,8 @@ suspend fun <T> Flow<T>.safeCollect(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun generateConfig(isLeft: Boolean): GamePadConfig {
|
private fun generateConfig(isLeft: Boolean): GamePadConfig {
|
||||||
val distance = 0.05f
|
val distance = 0.3f
|
||||||
|
val buttonScale = 1f
|
||||||
|
|
||||||
if (isLeft) {
|
if (isLeft) {
|
||||||
return GamePadConfig(
|
return GamePadConfig(
|
||||||
@ -240,9 +248,9 @@ private fun generateConfig(isLeft: Boolean): GamePadConfig {
|
|||||||
),
|
),
|
||||||
listOf(
|
listOf(
|
||||||
SecondaryDialConfig.Cross(
|
SecondaryDialConfig.Cross(
|
||||||
9,
|
10,
|
||||||
3,
|
3,
|
||||||
1.8f,
|
2.5f,
|
||||||
distance,
|
distance,
|
||||||
CrossConfig(
|
CrossConfig(
|
||||||
GamePadButtonInputId.DpadUp.ordinal,
|
GamePadButtonInputId.DpadUp.ordinal,
|
||||||
@ -256,9 +264,9 @@ private fun generateConfig(isLeft: Boolean): GamePadConfig {
|
|||||||
SecondaryDialConfig.RotationProcessor()
|
SecondaryDialConfig.RotationProcessor()
|
||||||
),
|
),
|
||||||
SecondaryDialConfig.SingleButton(
|
SecondaryDialConfig.SingleButton(
|
||||||
0,
|
1,
|
||||||
1f,
|
buttonScale,
|
||||||
0.05f,
|
distance,
|
||||||
ButtonConfig(
|
ButtonConfig(
|
||||||
GamePadButtonInputId.Minus.ordinal,
|
GamePadButtonInputId.Minus.ordinal,
|
||||||
"-",
|
"-",
|
||||||
@ -274,7 +282,7 @@ private fun generateConfig(isLeft: Boolean): GamePadConfig {
|
|||||||
),
|
),
|
||||||
SecondaryDialConfig.DoubleButton(
|
SecondaryDialConfig.DoubleButton(
|
||||||
2,
|
2,
|
||||||
0.05f,
|
distance,
|
||||||
ButtonConfig(
|
ButtonConfig(
|
||||||
GamePadButtonInputId.LeftShoulder.ordinal,
|
GamePadButtonInputId.LeftShoulder.ordinal,
|
||||||
"L",
|
"L",
|
||||||
@ -289,9 +297,9 @@ private fun generateConfig(isLeft: Boolean): GamePadConfig {
|
|||||||
SecondaryDialConfig.RotationProcessor()
|
SecondaryDialConfig.RotationProcessor()
|
||||||
),
|
),
|
||||||
SecondaryDialConfig.SingleButton(
|
SecondaryDialConfig.SingleButton(
|
||||||
8,
|
9,
|
||||||
1f,
|
buttonScale,
|
||||||
0.05f,
|
distance,
|
||||||
ButtonConfig(
|
ButtonConfig(
|
||||||
GamePadButtonInputId.LeftTrigger.ordinal,
|
GamePadButtonInputId.LeftTrigger.ordinal,
|
||||||
"ZL",
|
"ZL",
|
||||||
@ -362,8 +370,8 @@ private fun generateConfig(isLeft: Boolean): GamePadConfig {
|
|||||||
SecondaryDialConfig.Stick(
|
SecondaryDialConfig.Stick(
|
||||||
7,
|
7,
|
||||||
2,
|
2,
|
||||||
3f,
|
2f,
|
||||||
0.05f,
|
distance,
|
||||||
GamePadButtonInputId.RightStick.ordinal,
|
GamePadButtonInputId.RightStick.ordinal,
|
||||||
GamePadButtonInputId.RightStickButton.ordinal,
|
GamePadButtonInputId.RightStickButton.ordinal,
|
||||||
null,
|
null,
|
||||||
@ -373,8 +381,8 @@ private fun generateConfig(isLeft: Boolean): GamePadConfig {
|
|||||||
),
|
),
|
||||||
SecondaryDialConfig.SingleButton(
|
SecondaryDialConfig.SingleButton(
|
||||||
6,
|
6,
|
||||||
1f,
|
buttonScale,
|
||||||
0.05f,
|
distance,
|
||||||
ButtonConfig(
|
ButtonConfig(
|
||||||
GamePadButtonInputId.Plus.ordinal,
|
GamePadButtonInputId.Plus.ordinal,
|
||||||
"+",
|
"+",
|
||||||
@ -390,7 +398,7 @@ private fun generateConfig(isLeft: Boolean): GamePadConfig {
|
|||||||
),
|
),
|
||||||
SecondaryDialConfig.DoubleButton(
|
SecondaryDialConfig.DoubleButton(
|
||||||
3,
|
3,
|
||||||
0.05f,
|
distance,
|
||||||
ButtonConfig(
|
ButtonConfig(
|
||||||
GamePadButtonInputId.RightShoulder.ordinal,
|
GamePadButtonInputId.RightShoulder.ordinal,
|
||||||
"R",
|
"R",
|
||||||
@ -406,8 +414,8 @@ private fun generateConfig(isLeft: Boolean): GamePadConfig {
|
|||||||
),
|
),
|
||||||
SecondaryDialConfig.SingleButton(
|
SecondaryDialConfig.SingleButton(
|
||||||
9,
|
9,
|
||||||
1f,
|
buttonScale,
|
||||||
0.05f,
|
distance,
|
||||||
ButtonConfig(
|
ButtonConfig(
|
||||||
GamePadButtonInputId.RightTrigger.ordinal,
|
GamePadButtonInputId.RightTrigger.ordinal,
|
||||||
"ZR",
|
"ZR",
|
||||||
|
@ -23,6 +23,61 @@ class Icons {
|
|||||||
companion object {
|
companion object {
|
||||||
/// Icons exported from https://www.composables.com/icons
|
/// Icons exported from https://www.composables.com/icons
|
||||||
@Composable
|
@Composable
|
||||||
|
fun circle(color: Color): ImageVector {
|
||||||
|
return remember {
|
||||||
|
ImageVector.Builder(
|
||||||
|
name = "circle",
|
||||||
|
defaultWidth = 40.0.dp,
|
||||||
|
defaultHeight = 40.0.dp,
|
||||||
|
viewportWidth = 40.0f,
|
||||||
|
viewportHeight = 40.0f
|
||||||
|
).apply {
|
||||||
|
path(
|
||||||
|
fill = SolidColor(color),
|
||||||
|
fillAlpha = 1f,
|
||||||
|
stroke = null,
|
||||||
|
strokeAlpha = 1f,
|
||||||
|
strokeLineWidth = 1.0f,
|
||||||
|
strokeLineCap = StrokeCap.Butt,
|
||||||
|
strokeLineJoin = StrokeJoin.Miter,
|
||||||
|
strokeLineMiter = 1f,
|
||||||
|
pathFillType = PathFillType.NonZero
|
||||||
|
) {
|
||||||
|
moveTo(20f, 36.375f)
|
||||||
|
quadToRelative(-3.375f, 0f, -6.375f, -1.292f)
|
||||||
|
quadToRelative(-3f, -1.291f, -5.208f, -3.521f)
|
||||||
|
quadToRelative(-2.209f, -2.229f, -3.5f, -5.208f)
|
||||||
|
quadTo(3.625f, 23.375f, 3.625f, 20f)
|
||||||
|
quadToRelative(0f, -3.417f, 1.292f, -6.396f)
|
||||||
|
quadToRelative(1.291f, -2.979f, 3.521f, -5.208f)
|
||||||
|
quadToRelative(2.229f, -2.229f, 5.208f, -3.5f)
|
||||||
|
reflectiveQuadTo(20f, 3.625f)
|
||||||
|
quadToRelative(3.417f, 0f, 6.396f, 1.292f)
|
||||||
|
quadToRelative(2.979f, 1.291f, 5.208f, 3.5f)
|
||||||
|
quadToRelative(2.229f, 2.208f, 3.5f, 5.187f)
|
||||||
|
reflectiveQuadTo(36.375f, 20f)
|
||||||
|
quadToRelative(0f, 3.375f, -1.292f, 6.375f)
|
||||||
|
quadToRelative(-1.291f, 3f, -3.5f, 5.208f)
|
||||||
|
quadToRelative(-2.208f, 2.209f, -5.187f, 3.5f)
|
||||||
|
quadToRelative(-2.979f, 1.292f, -6.396f, 1.292f)
|
||||||
|
close()
|
||||||
|
moveToRelative(0f, -2.625f)
|
||||||
|
quadToRelative(5.75f, 0f, 9.75f, -4.021f)
|
||||||
|
reflectiveQuadToRelative(4f, -9.729f)
|
||||||
|
quadToRelative(0f, -5.75f, -4f, -9.75f)
|
||||||
|
reflectiveQuadToRelative(-9.75f, -4f)
|
||||||
|
quadToRelative(-5.708f, 0f, -9.729f, 4f)
|
||||||
|
quadToRelative(-4.021f, 4f, -4.021f, 9.75f)
|
||||||
|
quadToRelative(0f, 5.708f, 4.021f, 9.729f)
|
||||||
|
quadTo(14.292f, 33.75f, 20f, 33.75f)
|
||||||
|
close()
|
||||||
|
moveTo(20f, 20f)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Composable
|
||||||
fun listView(color: Color): ImageVector {
|
fun listView(color: Color): ImageVector {
|
||||||
return remember {
|
return remember {
|
||||||
ImageVector.Builder(
|
ImageVector.Builder(
|
||||||
|
@ -59,9 +59,14 @@ class GameModel(var file: DocumentFile, val context: Context) {
|
|||||||
val uri = Uri.parse(vm.data?.selected)
|
val uri = Uri.parse(vm.data?.selected)
|
||||||
val file = DocumentFile.fromSingleUri(context, uri)
|
val file = DocumentFile.fromSingleUri(context, uri)
|
||||||
if (file?.exists() == true) {
|
if (file?.exists() == true) {
|
||||||
updateDescriptor = context.contentResolver.openFileDescriptor(file.uri, "rw")
|
try {
|
||||||
|
updateDescriptor =
|
||||||
|
context.contentResolver.openFileDescriptor(file.uri, "rw")
|
||||||
|
|
||||||
return updateDescriptor?.fd ?: -1
|
return updateDescriptor?.fd ?: -1
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return -2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package org.ryujinx.android.viewmodels
|
package org.ryujinx.android.viewmodels
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
@ -8,6 +10,10 @@ import com.anggrayudi.storage.file.DocumentFileCompat
|
|||||||
import com.anggrayudi.storage.file.DocumentFileType
|
import com.anggrayudi.storage.file.DocumentFileType
|
||||||
import com.anggrayudi.storage.file.extension
|
import com.anggrayudi.storage.file.extension
|
||||||
import com.anggrayudi.storage.file.search
|
import com.anggrayudi.storage.file.search
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.ryujinx.android.MainActivity
|
import org.ryujinx.android.MainActivity
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
@ -18,11 +24,11 @@ class HomeViewModel(
|
|||||||
) {
|
) {
|
||||||
private var shouldReload: Boolean = false
|
private var shouldReload: Boolean = false
|
||||||
private var savedFolder: String = ""
|
private var savedFolder: String = ""
|
||||||
private var isLoading: Boolean = false
|
|
||||||
private var loadedCache: MutableList<GameModel> = mutableListOf()
|
private var loadedCache: MutableList<GameModel> = mutableListOf()
|
||||||
private var gameFolderPath: DocumentFile? = null
|
private var gameFolderPath: DocumentFile? = null
|
||||||
private var sharedPref: SharedPreferences? = null
|
private var sharedPref: SharedPreferences? = null
|
||||||
val gameList: SnapshotStateList<GameModel> = SnapshotStateList()
|
val gameList: SnapshotStateList<GameModel> = SnapshotStateList()
|
||||||
|
val isLoading: MutableState<Boolean> = mutableStateOf(false)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (activity != null) {
|
if (activity != null) {
|
||||||
@ -59,19 +65,21 @@ class HomeViewModel(
|
|||||||
shouldReload = true
|
shouldReload = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
private fun reloadGameList() {
|
private fun reloadGameList() {
|
||||||
activity?.storageHelper ?: return
|
activity?.storageHelper ?: return
|
||||||
|
|
||||||
if (isLoading)
|
|
||||||
return
|
|
||||||
val folder = gameFolderPath ?: return
|
val folder = gameFolderPath ?: return
|
||||||
|
|
||||||
gameList.clear()
|
shouldReload = false
|
||||||
|
if (isLoading.value)
|
||||||
|
return
|
||||||
|
|
||||||
|
gameList.clear()
|
||||||
|
loadedCache.clear()
|
||||||
|
isLoading.value = true
|
||||||
|
|
||||||
isLoading = true
|
|
||||||
thread {
|
thread {
|
||||||
try {
|
try {
|
||||||
loadedCache.clear()
|
|
||||||
for (file in folder.search(false, DocumentFileType.FILE)) {
|
for (file in folder.search(false, DocumentFileType.FILE)) {
|
||||||
if (file.extension == "xci" || file.extension == "nsp" || file.extension == "nro")
|
if (file.extension == "xci" || file.extension == "nsp" || file.extension == "nro")
|
||||||
activity.let {
|
activity.let {
|
||||||
@ -79,14 +87,14 @@ class HomeViewModel(
|
|||||||
|
|
||||||
if (item.titleId?.isNotEmpty() == true && item.titleName?.isNotEmpty() == true && item.titleName != "Unknown") {
|
if (item.titleId?.isNotEmpty() == true && item.titleName?.isNotEmpty() == true && item.titleName != "Unknown") {
|
||||||
loadedCache.add(item)
|
loadedCache.add(item)
|
||||||
gameList.add(item)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isLoading = false
|
|
||||||
} finally {
|
} finally {
|
||||||
isLoading = false
|
isLoading.value = false
|
||||||
|
GlobalScope.launch(Dispatchers.Main){
|
||||||
|
filter("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,14 +66,19 @@ class MainViewModel(val activity: MainActivity) {
|
|||||||
firmwareVersion = RyujinxNative.jnaInstance.deviceGetInstalledFirmwareVersion()
|
firmwareVersion = RyujinxNative.jnaInstance.deviceGetInstalledFirmwareVersion()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadGame(game: GameModel): Boolean {
|
fun loadGame(game: GameModel): Int {
|
||||||
val descriptor = game.open()
|
val descriptor = game.open()
|
||||||
|
|
||||||
if (descriptor == 0)
|
if (descriptor == 0)
|
||||||
return false
|
return 0
|
||||||
|
|
||||||
val update = game.openUpdate()
|
val update = game.openUpdate()
|
||||||
|
|
||||||
|
if(update == -2)
|
||||||
|
{
|
||||||
|
return -2
|
||||||
|
}
|
||||||
|
|
||||||
gameModel = game
|
gameModel = game
|
||||||
isMiiEditorLaunched = false
|
isMiiEditorLaunched = false
|
||||||
|
|
||||||
@ -87,7 +92,7 @@ class MainViewModel(val activity: MainActivity) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
return false
|
return 0
|
||||||
|
|
||||||
val nativeHelpers = NativeHelpers.instance
|
val nativeHelpers = NativeHelpers.instance
|
||||||
val nativeInterop = NativeGraphicsInterop()
|
val nativeInterop = NativeGraphicsInterop()
|
||||||
@ -141,7 +146,7 @@ class MainViewModel(val activity: MainActivity) {
|
|||||||
driverHandle
|
driverHandle
|
||||||
)
|
)
|
||||||
if (!success)
|
if (!success)
|
||||||
return false
|
return 0
|
||||||
|
|
||||||
val semaphore = Semaphore(1, 0)
|
val semaphore = Semaphore(1, 0)
|
||||||
runBlocking {
|
runBlocking {
|
||||||
@ -168,12 +173,12 @@ class MainViewModel(val activity: MainActivity) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
return false
|
return 0
|
||||||
|
|
||||||
success =
|
success =
|
||||||
RyujinxNative.jnaInstance.deviceLoadDescriptor(descriptor, game.type.ordinal, update)
|
RyujinxNative.jnaInstance.deviceLoadDescriptor(descriptor, game.type.ordinal, update)
|
||||||
|
|
||||||
return success
|
return if (success) 1 else 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadMiiEditor(): Boolean {
|
fun loadMiiEditor(): Boolean {
|
||||||
|
@ -19,6 +19,7 @@ class QuickSettings(val activity: Activity) {
|
|||||||
var useSwitchLayout: Boolean
|
var useSwitchLayout: Boolean
|
||||||
var enableMotion: Boolean
|
var enableMotion: Boolean
|
||||||
var enablePerformanceMode: Boolean
|
var enablePerformanceMode: Boolean
|
||||||
|
var controllerStickSensitivity: Float
|
||||||
|
|
||||||
// Logs
|
// Logs
|
||||||
var enableDebugLogs: Boolean
|
var enableDebugLogs: Boolean
|
||||||
@ -49,6 +50,7 @@ class QuickSettings(val activity: Activity) {
|
|||||||
useSwitchLayout = sharedPref.getBoolean("useSwitchLayout", true)
|
useSwitchLayout = sharedPref.getBoolean("useSwitchLayout", true)
|
||||||
enableMotion = sharedPref.getBoolean("enableMotion", true)
|
enableMotion = sharedPref.getBoolean("enableMotion", true)
|
||||||
enablePerformanceMode = sharedPref.getBoolean("enablePerformanceMode", true)
|
enablePerformanceMode = sharedPref.getBoolean("enablePerformanceMode", true)
|
||||||
|
controllerStickSensitivity = sharedPref.getFloat("controllerStickSensitivity", 1.0f)
|
||||||
|
|
||||||
enableDebugLogs = sharedPref.getBoolean("enableDebugLogs", false)
|
enableDebugLogs = sharedPref.getBoolean("enableDebugLogs", false)
|
||||||
enableStubLogs = sharedPref.getBoolean("enableStubLogs", false)
|
enableStubLogs = sharedPref.getBoolean("enableStubLogs", false)
|
||||||
@ -78,6 +80,7 @@ class QuickSettings(val activity: Activity) {
|
|||||||
editor.putBoolean("useSwitchLayout", useSwitchLayout)
|
editor.putBoolean("useSwitchLayout", useSwitchLayout)
|
||||||
editor.putBoolean("enableMotion", enableMotion)
|
editor.putBoolean("enableMotion", enableMotion)
|
||||||
editor.putBoolean("enablePerformanceMode", enablePerformanceMode)
|
editor.putBoolean("enablePerformanceMode", enablePerformanceMode)
|
||||||
|
editor.putFloat("enablePerformanceMode", controllerStickSensitivity)
|
||||||
|
|
||||||
editor.putBoolean("enableDebugLogs", enableDebugLogs)
|
editor.putBoolean("enableDebugLogs", enableDebugLogs)
|
||||||
editor.putBoolean("enableStubLogs", enableStubLogs)
|
editor.putBoolean("enableStubLogs", enableStubLogs)
|
||||||
|
@ -56,6 +56,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
|
|||||||
useSwitchLayout: MutableState<Boolean>,
|
useSwitchLayout: MutableState<Boolean>,
|
||||||
enableMotion: MutableState<Boolean>,
|
enableMotion: MutableState<Boolean>,
|
||||||
enablePerformanceMode: MutableState<Boolean>,
|
enablePerformanceMode: MutableState<Boolean>,
|
||||||
|
controllerStickSensitivity: MutableState<Float>,
|
||||||
enableDebugLogs: MutableState<Boolean>,
|
enableDebugLogs: MutableState<Boolean>,
|
||||||
enableStubLogs: MutableState<Boolean>,
|
enableStubLogs: MutableState<Boolean>,
|
||||||
enableInfoLogs: MutableState<Boolean>,
|
enableInfoLogs: MutableState<Boolean>,
|
||||||
@ -82,6 +83,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
|
|||||||
useSwitchLayout.value = sharedPref.getBoolean("useSwitchLayout", true)
|
useSwitchLayout.value = sharedPref.getBoolean("useSwitchLayout", true)
|
||||||
enableMotion.value = sharedPref.getBoolean("enableMotion", true)
|
enableMotion.value = sharedPref.getBoolean("enableMotion", true)
|
||||||
enablePerformanceMode.value = sharedPref.getBoolean("enablePerformanceMode", false)
|
enablePerformanceMode.value = sharedPref.getBoolean("enablePerformanceMode", false)
|
||||||
|
controllerStickSensitivity.value = sharedPref.getFloat("controllerStickSensitivity", 1.0f)
|
||||||
|
|
||||||
enableDebugLogs.value = sharedPref.getBoolean("enableDebugLogs", false)
|
enableDebugLogs.value = sharedPref.getBoolean("enableDebugLogs", false)
|
||||||
enableStubLogs.value = sharedPref.getBoolean("enableStubLogs", false)
|
enableStubLogs.value = sharedPref.getBoolean("enableStubLogs", false)
|
||||||
@ -109,6 +111,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
|
|||||||
useSwitchLayout: MutableState<Boolean>,
|
useSwitchLayout: MutableState<Boolean>,
|
||||||
enableMotion: MutableState<Boolean>,
|
enableMotion: MutableState<Boolean>,
|
||||||
enablePerformanceMode: MutableState<Boolean>,
|
enablePerformanceMode: MutableState<Boolean>,
|
||||||
|
controllerStickSensitivity: MutableState<Float>,
|
||||||
enableDebugLogs: MutableState<Boolean>,
|
enableDebugLogs: MutableState<Boolean>,
|
||||||
enableStubLogs: MutableState<Boolean>,
|
enableStubLogs: MutableState<Boolean>,
|
||||||
enableInfoLogs: MutableState<Boolean>,
|
enableInfoLogs: MutableState<Boolean>,
|
||||||
@ -135,6 +138,7 @@ class SettingsViewModel(var navController: NavHostController, val activity: Main
|
|||||||
editor.putBoolean("useSwitchLayout", useSwitchLayout.value)
|
editor.putBoolean("useSwitchLayout", useSwitchLayout.value)
|
||||||
editor.putBoolean("enableMotion", enableMotion.value)
|
editor.putBoolean("enableMotion", enableMotion.value)
|
||||||
editor.putBoolean("enablePerformanceMode", enablePerformanceMode.value)
|
editor.putBoolean("enablePerformanceMode", enablePerformanceMode.value)
|
||||||
|
editor.putFloat("controllerStickSensitivity", controllerStickSensitivity.value)
|
||||||
|
|
||||||
editor.putBoolean("enableDebugLogs", enableDebugLogs.value)
|
editor.putBoolean("enableDebugLogs", enableDebugLogs.value)
|
||||||
editor.putBoolean("enableStubLogs", enableStubLogs.value)
|
editor.putBoolean("enableStubLogs", enableStubLogs.value)
|
||||||
|
@ -19,8 +19,8 @@ class TitleUpdateViewModel(val titleId: String) {
|
|||||||
private var basePath: String
|
private var basePath: String
|
||||||
private var updateJsonName = "updates.json"
|
private var updateJsonName = "updates.json"
|
||||||
private var storageHelper: SimpleStorageHelper
|
private var storageHelper: SimpleStorageHelper
|
||||||
var currentPaths: MutableList<String> = mutableListOf()
|
private var currentPaths: MutableList<String> = mutableListOf()
|
||||||
var pathsState: SnapshotStateList<String>? = null
|
private var pathsState: SnapshotStateList<String>? = null
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val UpdateRequestCode = 1002
|
const val UpdateRequestCode = 1002
|
||||||
|
@ -2,13 +2,13 @@ package org.ryujinx.android.viewmodels
|
|||||||
|
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import com.anggrayudi.storage.file.extension
|
import com.anggrayudi.storage.file.extension
|
||||||
import com.anggrayudi.storage.file.getAbsolutePath
|
import com.anggrayudi.storage.file.openInputStream
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import org.ryujinx.android.MainActivity
|
import org.ryujinx.android.MainActivity
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipInputStream
|
||||||
|
|
||||||
class VulkanDriverViewModel(val activity: MainActivity) {
|
class VulkanDriverViewModel(val activity: MainActivity) {
|
||||||
var selected: String = ""
|
var selected: String = ""
|
||||||
@ -105,39 +105,37 @@ class VulkanDriverViewModel(val activity: MainActivity) {
|
|||||||
if (requestCode == DriverRequestCode) {
|
if (requestCode == DriverRequestCode) {
|
||||||
val file = files.firstOrNull()
|
val file = files.firstOrNull()
|
||||||
file?.apply {
|
file?.apply {
|
||||||
val path = file.getAbsolutePath(storage.context)
|
val stream = file.openInputStream(storage.context)
|
||||||
if (path.isNotEmpty()) {
|
stream?.apply {
|
||||||
val name = file.name?.removeSuffix("." + file.extension) ?: ""
|
val name = file.name?.removeSuffix("." + file.extension) ?: ""
|
||||||
val driverFolder = ensureDriverPath()
|
val driverFolder = ensureDriverPath()
|
||||||
val extractionFolder = File(driverFolder.absolutePath + "/${name}")
|
val extractionFolder = File(driverFolder.absolutePath + "/${name}")
|
||||||
extractionFolder.deleteRecursively()
|
extractionFolder.deleteRecursively()
|
||||||
extractionFolder.mkdirs()
|
extractionFolder.mkdirs()
|
||||||
ZipFile(path).use { zip ->
|
ZipInputStream(stream).use { zip ->
|
||||||
zip.entries().asSequence().forEach { entry ->
|
var entry = zip.nextEntry
|
||||||
|
while (entry != null) {
|
||||||
|
val filePath =
|
||||||
|
extractionFolder.absolutePath + File.separator + entry.name
|
||||||
|
|
||||||
zip.getInputStream(entry).use { input ->
|
if (!entry.isDirectory) {
|
||||||
val filePath =
|
File(filePath).delete()
|
||||||
extractionFolder.absolutePath + File.separator + entry.name
|
val bos =
|
||||||
|
BufferedOutputStream(FileOutputStream(filePath))
|
||||||
if (!entry.isDirectory) {
|
val bytesIn = ByteArray(4096)
|
||||||
File(filePath).delete()
|
var read: Int
|
||||||
val bos =
|
while (zip.read(bytesIn)
|
||||||
BufferedOutputStream(FileOutputStream(filePath))
|
.also { read = it } != -1
|
||||||
val bytesIn = ByteArray(4096)
|
) {
|
||||||
var read: Int
|
bos.write(bytesIn, 0, read)
|
||||||
while (input.read(bytesIn)
|
|
||||||
.also { read = it } != -1
|
|
||||||
) {
|
|
||||||
bos.write(bytesIn, 0, read)
|
|
||||||
}
|
|
||||||
bos.close()
|
|
||||||
} else {
|
|
||||||
val dir = File(filePath)
|
|
||||||
dir.mkdir()
|
|
||||||
}
|
}
|
||||||
|
bos.close()
|
||||||
|
} else {
|
||||||
|
val dir = File(filePath)
|
||||||
|
dir.mkdir()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entry = zip.nextEntry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ package org.ryujinx.android.views
|
|||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutVertically
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.basicMarquee
|
import androidx.compose.foundation.basicMarquee
|
||||||
@ -32,15 +34,17 @@ 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.Menu
|
import androidx.compose.material.icons.filled.Menu
|
||||||
import androidx.compose.material.icons.filled.Person
|
import androidx.compose.material.icons.filled.Person
|
||||||
|
import androidx.compose.material.icons.filled.Refresh
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.material3.AlertDialog
|
|
||||||
import androidx.compose.material3.AlertDialogDefaults
|
import androidx.compose.material3.AlertDialogDefaults
|
||||||
import androidx.compose.material3.BasicAlertDialog
|
import androidx.compose.material3.BasicAlertDialog
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.FloatingActionButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
@ -61,8 +65,12 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.asImageBitmap
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||||
|
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
@ -99,6 +107,9 @@ class HomeViews {
|
|||||||
val canClose = remember { mutableStateOf(true) }
|
val canClose = remember { mutableStateOf(true) }
|
||||||
val openDlcDialog = remember { mutableStateOf(false) }
|
val openDlcDialog = remember { mutableStateOf(false) }
|
||||||
var openAppBarExtra by remember { mutableStateOf(false) }
|
var openAppBarExtra by remember { mutableStateOf(false) }
|
||||||
|
val showError = remember {
|
||||||
|
mutableStateOf("")
|
||||||
|
}
|
||||||
|
|
||||||
val selectedModel = remember {
|
val selectedModel = remember {
|
||||||
mutableStateOf(viewModel.mainViewModel?.selected)
|
mutableStateOf(viewModel.mainViewModel?.selected)
|
||||||
@ -110,6 +121,24 @@ class HomeViews {
|
|||||||
mutableStateOf(true)
|
mutableStateOf(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isFabVisible by remember {
|
||||||
|
mutableStateOf(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
val nestedScrollConnection = remember {
|
||||||
|
object : NestedScrollConnection {
|
||||||
|
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
|
||||||
|
if (available.y < -1) {
|
||||||
|
isFabVisible = false
|
||||||
|
}
|
||||||
|
if (available.y > 1) {
|
||||||
|
isFabVisible = true
|
||||||
|
}
|
||||||
|
return Offset.Zero
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
topBar = {
|
topBar = {
|
||||||
@ -170,6 +199,21 @@ class HomeViews {
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
floatingActionButton = {
|
||||||
|
AnimatedVisibility(visible = isFabVisible,
|
||||||
|
enter = slideInVertically(initialOffsetY = { it * 2 }),
|
||||||
|
exit = slideOutVertically(targetOffsetY = { it * 2 })) {
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
viewModel.requestReload()
|
||||||
|
viewModel.ensureReloadIfNecessary()
|
||||||
|
},
|
||||||
|
shape = MaterialTheme.shapes.small
|
||||||
|
) {
|
||||||
|
Icon(Icons.Default.Refresh, contentDescription = "refresh")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
) { contentPadding ->
|
) { contentPadding ->
|
||||||
@ -309,56 +353,75 @@ class HomeViews {
|
|||||||
val list = remember {
|
val list = remember {
|
||||||
viewModel.gameList
|
viewModel.gameList
|
||||||
}
|
}
|
||||||
|
val isLoading = remember {
|
||||||
|
viewModel.isLoading
|
||||||
|
}
|
||||||
viewModel.filter(query.value)
|
viewModel.filter(query.value)
|
||||||
|
|
||||||
if (!isPreview) {
|
if (!isPreview) {
|
||||||
var settings = QuickSettings(viewModel.activity!!)
|
var settings = QuickSettings(viewModel.activity!!)
|
||||||
|
|
||||||
if (settings.isGrid) {
|
if (isLoading.value) {
|
||||||
val size =
|
Box(modifier = Modifier.fillMaxSize())
|
||||||
GridImageSize / Resources.getSystem().displayMetrics.density
|
{
|
||||||
LazyVerticalGrid(
|
CircularProgressIndicator(
|
||||||
columns = GridCells.Adaptive(minSize = (size + 4).dp),
|
modifier = Modifier
|
||||||
modifier = Modifier
|
.width(64.dp)
|
||||||
.fillMaxSize()
|
.align(Alignment.Center),
|
||||||
.padding(4.dp),
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
horizontalArrangement = Arrangement.SpaceEvenly
|
trackColor = MaterialTheme.colorScheme.surfaceVariant
|
||||||
) {
|
)
|
||||||
items(list) {
|
|
||||||
it.titleName?.apply {
|
|
||||||
if (this.isNotEmpty() && (query.value.trim()
|
|
||||||
.isEmpty() || this.lowercase(Locale.getDefault())
|
|
||||||
.contains(query.value))
|
|
||||||
)
|
|
||||||
GridGameItem(
|
|
||||||
it,
|
|
||||||
viewModel,
|
|
||||||
showAppActions,
|
|
||||||
showLoading,
|
|
||||||
selectedModel
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LazyColumn(Modifier.fillMaxSize()) {
|
if (settings.isGrid) {
|
||||||
items(list) {
|
val size =
|
||||||
it.titleName?.apply {
|
GridImageSize / Resources.getSystem().displayMetrics.density
|
||||||
if (this.isNotEmpty() && (query.value.trim()
|
LazyVerticalGrid(
|
||||||
.isEmpty() || this.lowercase(
|
columns = GridCells.Adaptive(minSize = (size + 4).dp),
|
||||||
Locale.getDefault()
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(4.dp)
|
||||||
|
.nestedScroll(nestedScrollConnection),
|
||||||
|
horizontalArrangement = Arrangement.SpaceEvenly
|
||||||
|
) {
|
||||||
|
items(list) {
|
||||||
|
it.titleName?.apply {
|
||||||
|
if (this.isNotEmpty() && (query.value.trim()
|
||||||
|
.isEmpty() || this.lowercase(Locale.getDefault())
|
||||||
|
.contains(query.value))
|
||||||
)
|
)
|
||||||
.contains(query.value))
|
GridGameItem(
|
||||||
)
|
|
||||||
Box(modifier = Modifier.animateItemPlacement()) {
|
|
||||||
ListGameItem(
|
|
||||||
it,
|
it,
|
||||||
viewModel,
|
viewModel,
|
||||||
showAppActions,
|
showAppActions,
|
||||||
showLoading,
|
showLoading,
|
||||||
selectedModel,
|
selectedModel,
|
||||||
|
showError
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LazyColumn(Modifier.fillMaxSize()) {
|
||||||
|
items(list) {
|
||||||
|
it.titleName?.apply {
|
||||||
|
if (this.isNotEmpty() && (query.value.trim()
|
||||||
|
.isEmpty() || this.lowercase(
|
||||||
|
Locale.getDefault()
|
||||||
|
)
|
||||||
|
.contains(query.value))
|
||||||
|
)
|
||||||
|
Box(modifier = Modifier.animateItemPlacement()) {
|
||||||
|
ListGameItem(
|
||||||
|
it,
|
||||||
|
viewModel,
|
||||||
|
showAppActions,
|
||||||
|
showLoading,
|
||||||
|
selectedModel,
|
||||||
|
showError
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -443,11 +506,14 @@ class HomeViews {
|
|||||||
showLoading.value = true
|
showLoading.value = true
|
||||||
val success =
|
val success =
|
||||||
viewModel.mainViewModel.loadGame(viewModel.mainViewModel.selected!!)
|
viewModel.mainViewModel.loadGame(viewModel.mainViewModel.selected!!)
|
||||||
if (success) {
|
if (success == 1) {
|
||||||
launchOnUiThread {
|
launchOnUiThread {
|
||||||
viewModel.mainViewModel.navigateToGame()
|
viewModel.mainViewModel.navigateToGame()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (success == -2)
|
||||||
|
showError.value =
|
||||||
|
"Error loading update. Please re-add update file"
|
||||||
viewModel.mainViewModel.selected!!.close()
|
viewModel.mainViewModel.selected!!.close()
|
||||||
}
|
}
|
||||||
showLoading.value = false
|
showLoading.value = false
|
||||||
@ -527,7 +593,8 @@ class HomeViews {
|
|||||||
viewModel: HomeViewModel,
|
viewModel: HomeViewModel,
|
||||||
showAppActions: MutableState<Boolean>,
|
showAppActions: MutableState<Boolean>,
|
||||||
showLoading: MutableState<Boolean>,
|
showLoading: MutableState<Boolean>,
|
||||||
selectedModel: MutableState<GameModel?>
|
selectedModel: MutableState<GameModel?>,
|
||||||
|
showError: MutableState<String>
|
||||||
) {
|
) {
|
||||||
remember {
|
remember {
|
||||||
selectedModel
|
selectedModel
|
||||||
@ -555,11 +622,14 @@ class HomeViews {
|
|||||||
showLoading.value = true
|
showLoading.value = true
|
||||||
val success =
|
val success =
|
||||||
viewModel.mainViewModel?.loadGame(gameModel) ?: false
|
viewModel.mainViewModel?.loadGame(gameModel) ?: false
|
||||||
if (success) {
|
if (success == 1) {
|
||||||
launchOnUiThread {
|
launchOnUiThread {
|
||||||
viewModel.mainViewModel?.navigateToGame()
|
viewModel.mainViewModel?.navigateToGame()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (success == -2)
|
||||||
|
showError.value =
|
||||||
|
"Error loading update. Please re-add update file"
|
||||||
gameModel.close()
|
gameModel.close()
|
||||||
}
|
}
|
||||||
showLoading.value = false
|
showLoading.value = false
|
||||||
@ -618,7 +688,8 @@ class HomeViews {
|
|||||||
viewModel: HomeViewModel,
|
viewModel: HomeViewModel,
|
||||||
showAppActions: MutableState<Boolean>,
|
showAppActions: MutableState<Boolean>,
|
||||||
showLoading: MutableState<Boolean>,
|
showLoading: MutableState<Boolean>,
|
||||||
selectedModel: MutableState<GameModel?>
|
selectedModel: MutableState<GameModel?>,
|
||||||
|
showError: MutableState<String>
|
||||||
) {
|
) {
|
||||||
remember {
|
remember {
|
||||||
selectedModel
|
selectedModel
|
||||||
@ -646,11 +717,14 @@ class HomeViews {
|
|||||||
showLoading.value = true
|
showLoading.value = true
|
||||||
val success =
|
val success =
|
||||||
viewModel.mainViewModel?.loadGame(gameModel) ?: false
|
viewModel.mainViewModel?.loadGame(gameModel) ?: false
|
||||||
if (success) {
|
if (success == 1) {
|
||||||
launchOnUiThread {
|
launchOnUiThread {
|
||||||
viewModel.mainViewModel?.navigateToGame()
|
viewModel.mainViewModel?.navigateToGame()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (success == -2)
|
||||||
|
showError.value =
|
||||||
|
"Error loading update. Please re-add update file"
|
||||||
gameModel.close()
|
gameModel.close()
|
||||||
}
|
}
|
||||||
showLoading.value = false
|
showLoading.value = false
|
||||||
|
@ -15,6 +15,7 @@ import androidx.compose.animation.fadeIn
|
|||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.shrinkVertically
|
import androidx.compose.animation.shrinkVertically
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||||
@ -24,6 +25,9 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.sizeIn
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.foundation.layout.wrapContentWidth
|
import androidx.compose.foundation.layout.wrapContentWidth
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
@ -31,16 +35,18 @@ import androidx.compose.foundation.verticalScroll
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
||||||
import androidx.compose.material3.AlertDialog
|
|
||||||
import androidx.compose.material3.AlertDialogDefaults
|
import androidx.compose.material3.AlertDialogDefaults
|
||||||
import androidx.compose.material3.BasicAlertDialog
|
import androidx.compose.material3.BasicAlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.Label
|
||||||
import androidx.compose.material3.LinearProgressIndicator
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.PlainTooltip
|
||||||
import androidx.compose.material3.RadioButton
|
import androidx.compose.material3.RadioButton
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Slider
|
import androidx.compose.material3.Slider
|
||||||
@ -125,6 +131,7 @@ class SettingViews {
|
|||||||
val useSwitchLayout = remember { mutableStateOf(true) }
|
val useSwitchLayout = remember { mutableStateOf(true) }
|
||||||
val enableMotion = remember { mutableStateOf(true) }
|
val enableMotion = remember { mutableStateOf(true) }
|
||||||
val enablePerformanceMode = remember { mutableStateOf(true) }
|
val enablePerformanceMode = remember { mutableStateOf(true) }
|
||||||
|
val controllerStickSensitivity = remember { mutableStateOf(1.0f) }
|
||||||
|
|
||||||
val enableDebugLogs = remember { mutableStateOf(true) }
|
val enableDebugLogs = remember { mutableStateOf(true) }
|
||||||
val enableStubLogs = remember { mutableStateOf(true) }
|
val enableStubLogs = remember { mutableStateOf(true) }
|
||||||
@ -149,6 +156,7 @@ class SettingViews {
|
|||||||
useSwitchLayout,
|
useSwitchLayout,
|
||||||
enableMotion,
|
enableMotion,
|
||||||
enablePerformanceMode,
|
enablePerformanceMode,
|
||||||
|
controllerStickSensitivity,
|
||||||
enableDebugLogs,
|
enableDebugLogs,
|
||||||
enableStubLogs,
|
enableStubLogs,
|
||||||
enableInfoLogs,
|
enableInfoLogs,
|
||||||
@ -184,6 +192,7 @@ class SettingViews {
|
|||||||
useSwitchLayout,
|
useSwitchLayout,
|
||||||
enableMotion,
|
enableMotion,
|
||||||
enablePerformanceMode,
|
enablePerformanceMode,
|
||||||
|
controllerStickSensitivity,
|
||||||
enableDebugLogs,
|
enableDebugLogs,
|
||||||
enableStubLogs,
|
enableStubLogs,
|
||||||
enableInfoLogs,
|
enableInfoLogs,
|
||||||
@ -926,6 +935,49 @@ class SettingViews {
|
|||||||
useSwitchLayout.value = !useSwitchLayout.value
|
useSwitchLayout.value = !useSwitchLayout.value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(8.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Controller Stick Sensitivity",
|
||||||
|
modifier = Modifier.align(Alignment.CenterVertically)
|
||||||
|
)
|
||||||
|
Slider(modifier = Modifier.width(250.dp), value = controllerStickSensitivity.value, onValueChange = {
|
||||||
|
controllerStickSensitivity.value = it
|
||||||
|
}, valueRange = 0.1f..2f,
|
||||||
|
steps = 20,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
thumb = {
|
||||||
|
Label(
|
||||||
|
label = {
|
||||||
|
PlainTooltip(modifier = Modifier
|
||||||
|
.sizeIn(45.dp, 25.dp)
|
||||||
|
.wrapContentWidth()) {
|
||||||
|
Text("%.2f".format(controllerStickSensitivity.value))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
interactionSource = interactionSource
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = org.ryujinx.android.Icons.circle(
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(ButtonDefaults.IconSize),
|
||||||
|
tint = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExpandableView(onCardArrowClick = { }, title = "Log") {
|
ExpandableView(onCardArrowClick = { }, title = "Log") {
|
||||||
@ -1094,6 +1146,7 @@ class SettingViews {
|
|||||||
useSwitchLayout,
|
useSwitchLayout,
|
||||||
enableMotion,
|
enableMotion,
|
||||||
enablePerformanceMode,
|
enablePerformanceMode,
|
||||||
|
controllerStickSensitivity,
|
||||||
enableDebugLogs,
|
enableDebugLogs,
|
||||||
enableStubLogs,
|
enableStubLogs,
|
||||||
enableInfoLogs,
|
enableInfoLogs,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user