From 953559c71c6d2cb82293e4cb90d5b72228ee8a62 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Sat, 8 Jul 2023 19:36:30 +0000 Subject: [PATCH] initial work on multi driver selection --- .../viewmodels/TitleUpdateViewModel.kt | 44 ++--- .../viewmodels/VulkanDriverViewModel.kt | 159 ++++++++++++++++++ .../org/ryujinx/android/views/SettingViews.kt | 156 +++++++++++++++++ 3 files changed, 338 insertions(+), 21 deletions(-) create mode 100644 src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/VulkanDriverViewModel.kt diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/TitleUpdateViewModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/TitleUpdateViewModel.kt index 670871572..7ea807b29 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/TitleUpdateViewModel.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/TitleUpdateViewModel.kt @@ -31,6 +31,29 @@ class TitleUpdateViewModel(val titleId: String) { } fun Add() { + var callBack = storageHelper.onFileSelected + + storageHelper.onFileSelected = { requestCode, files -> + run { + storageHelper.onFileSelected = callBack + if(requestCode == UpdateRequestCode) + { + var file = files.firstOrNull() + file?.apply { + var path = Helpers.getPath(storageHelper.storage.context, file.uri) + if(!path.isNullOrEmpty()){ + data?.apply { + if(!paths.contains(path)) { + paths.add(path) + pathsState?.clear() + pathsState?.addAll(paths) + } + } + } + } + } + } + } storageHelper.openFilePicker(UpdateRequestCode) } @@ -86,27 +109,6 @@ class TitleUpdateViewModel(val titleId: String) { } storageHelper = MainActivity.StorageHelper!! - - storageHelper.onFileSelected = { requestCode, files -> - run { - if(requestCode == UpdateRequestCode) - { - var file = files.firstOrNull() - file?.apply { - var path = Helpers.getPath(storageHelper.storage.context, file.uri) - if(!path.isNullOrEmpty()){ - data?.apply { - if(!paths.contains(path)) { - paths.add(path) - pathsState?.clear() - pathsState?.addAll(paths) - } - } - } - } - } - } - } } } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/VulkanDriverViewModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/VulkanDriverViewModel.kt new file mode 100644 index 000000000..fc9fffaa9 --- /dev/null +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/VulkanDriverViewModel.kt @@ -0,0 +1,159 @@ +package org.ryujinx.android.viewmodels + +import androidx.compose.runtime.MutableState +import com.anggrayudi.storage.file.extension +import com.google.gson.Gson +import org.ryujinx.android.Helpers +import org.ryujinx.android.MainActivity +import java.io.File +import java.util.zip.ZipFile + +class VulkanDriverViewModel(val activity: MainActivity) { + var selected: String = "" + + companion object { + val DriverRequestCode: Int = 1003 + const val DriverFolder: String = "drivers" + } + + private fun getAppPath() : String { + var appPath = + (MainActivity.AppPath ?: activity.getExternalFilesDir(null)?.absolutePath ?: ""); + appPath += "/" + + return appPath + } + + fun ensureDriverPath() : File { + var driverPath = getAppPath() + DriverFolder + + var driverFolder = File(driverPath) + + if(!driverFolder.exists()) + driverFolder.mkdirs() + + return driverFolder + } + + fun getAvailableDrivers() : MutableList { + var driverFolder = ensureDriverPath() + + var folders = driverFolder.walkTopDown() + + var drivers = mutableListOf() + + var selectedDriverFile = File(driverFolder.absolutePath + "/selected"); + if(selectedDriverFile.exists()){ + selected = selectedDriverFile.readText() + + if(!File(selected).exists()) { + selected = "" + saveSelected() + } + } + + var gson = Gson() + + for (folder in folders){ + if(folder.isDirectory() && folder.parent == driverFolder.absolutePath){ + var meta = File(folder.absolutePath + "/meta.json") + + if(meta.exists()){ + var metadata = gson.fromJson(meta.readText(), DriverMetadata::class.java) + if(metadata.name.isNotEmpty()) { + var driver = folder.absolutePath + "/${metadata.libraryName}" + metadata.driverPath = driver + if (File(driver).exists()) + drivers.add(metadata) + } + } + } + } + + return drivers + } + + fun saveSelected() { + var driverFolder = ensureDriverPath() + + var selectedDriverFile = File(driverFolder.absolutePath + "/selected") + selectedDriverFile.writeText(selected) + } + + fun removeSelected(){ + if(selected.isNotEmpty()){ + var sel = File(selected) + if(sel.exists()) { + var parent = sel.parentFile + parent.deleteRecursively() + } + selected = "" + + saveSelected() + } + } + + fun add(refresh: MutableState) { + activity.storageHelper?.apply { + + var callBack = this.onFileSelected + + onFileSelected = { requestCode, files -> + run { + onFileSelected = callBack + if(requestCode == DriverRequestCode) + { + var file = files.firstOrNull() + file?.apply { + var path = Helpers.getPath(storage.context, file.uri) + if(!path.isNullOrEmpty()){ + var name = file.name?.removeSuffix("." + file.extension) ?: "" + var driverFolder = ensureDriverPath() + var extractionFolder = File(driverFolder.absolutePath + "/${name}") + extractionFolder.mkdirs() + ZipFile(path)?.use { zip -> + zip.entries().asSequence().forEach { entry -> + + zip.getInputStream(entry).use { input -> + val filePath = extractionFolder.absolutePath + File.separator + entry.name + + if (!entry.isDirectory) { + var length = input.available() + val bytesIn = ByteArray(length) + input.read(bytesIn) + File(filePath).writeBytes(bytesIn) + } else { + val dir = File(filePath) + dir.mkdir() + } + + } + + } + } + } + } + + refresh.value = true + } + } + } + openFilePicker(DriverRequestCode, + filterMimeTypes = arrayOf("application/zip") + ) + } + } +} + +data class DriverMetadata( + var schemaVersion : Int = 0, + var name : String = "", + var description : String = "", + var author : String = "", + var packageVersion : String = "", + var vendor : String = "", + var driverVersion : String = "", + var minApi : Int = 0, + var libraryName : String = "", + var driverPath : String = "" +) \ No newline at end of file diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt index 68cfd3416..3546fe7b5 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/SettingViews.kt @@ -13,24 +13,34 @@ import androidx.compose.animation.expandVertically import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.shrinkVertically +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.KeyboardArrowUp +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.AlertDialogDefaults +import androidx.compose.material3.Button 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.RadioButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Slider +import androidx.compose.material3.Surface 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 @@ -42,6 +52,7 @@ 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 +import org.ryujinx.android.viewmodels.VulkanDriverViewModel class SettingViews { companion object { @@ -273,6 +284,151 @@ class SettingViews { enableTextureRecompression.value = !enableTextureRecompression.value }) } + /*Row( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp), + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.CenterVertically + ) { + var isDriverSelectorOpen = remember { + mutableStateOf(false) + } + var driverViewModel = VulkanDriverViewModel(settingsViewModel.activity) + var isChanged = remember { + mutableStateOf(false) + } + var refresh = remember { + mutableStateOf(false) + } + var drivers = driverViewModel.getAvailableDrivers() + var selectedDriver = remember { + mutableStateOf(0) + } + + if(refresh.value) { + isChanged.value = true + refresh.value = false + } + + if(isDriverSelectorOpen.value){ + AlertDialog(onDismissRequest = { + isDriverSelectorOpen.value = false + + if(isChanged.value){ + driverViewModel.saveSelected() + } + }) { + Column { + Surface( + modifier = Modifier + .wrapContentWidth() + .wrapContentHeight(), + shape = MaterialTheme.shapes.large, + tonalElevation = AlertDialogDefaults.TonalElevation + ) { + if (!isChanged.value) { + selectedDriver.value = + drivers.indexOfFirst { it.driverPath == driverViewModel.selected } + 1 + isChanged.value = true + } + Column { + Column (modifier = Modifier + .fillMaxWidth() + .height(300.dp)) { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + RadioButton( + selected = selectedDriver.value == 0 || driverViewModel.selected.isEmpty(), + onClick = { + selectedDriver.value = 0 + isChanged.value = true + driverViewModel.selected = "" + }) + Column { + Text(text = "Default", + modifier = Modifier + .fillMaxWidth() + .clickable { + selectedDriver.value = 0 + isChanged.value = true + driverViewModel.selected = + "" + }) + } + } + var driverIndex = 1 + for (driver in drivers) { + var ind = driverIndex + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + RadioButton( + selected = selectedDriver.value == ind, + onClick = { + selectedDriver.value = ind + isChanged.value = true + driverViewModel.selected = + driver.driverPath + }) + Column { + Text(text = driver.libraryName, + modifier = Modifier + .fillMaxWidth() + .clickable { + selectedDriver.value = + ind + isChanged.value = + true + driverViewModel.selected = + driver.driverPath + }) + } + } + + driverIndex++ + } + } + Row( + horizontalArrangement = Arrangement.End, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Button(onClick = { + driverViewModel.removeSelected() + refresh.value = true + }, modifier = Modifier.padding(8.dp)) { + Text(text = "Remove") + } + + Button(onClick = { + driverViewModel.add(refresh) + refresh.value = true + }, modifier = Modifier.padding(8.dp)) { + Text(text = "Add") + } + } + } + } + } + } + } + + TextButton( + { + isChanged.value = false + isDriverSelectorOpen.value = !isDriverSelectorOpen.value + }, + modifier = Modifier.align(Alignment.CenterVertically) + ){ + Text(text = "Drivers") + } + } + */ } } }