forked from MeloNX/MeloNX
initial work on multi driver selection
This commit is contained in:
parent
d6ba9ad8f5
commit
953559c71c
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<DriverMetadata> {
|
||||
var driverFolder = ensureDriverPath()
|
||||
|
||||
var folders = driverFolder.walkTopDown()
|
||||
|
||||
var drivers = mutableListOf<DriverMetadata>()
|
||||
|
||||
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<Boolean>) {
|
||||
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 = ""
|
||||
)
|
@ -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")
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user