forked from MeloNX/MeloNX
create a copy of updates when added
This commit is contained in:
parent
7a2fadb499
commit
091d9d4546
@ -1,16 +1,30 @@
|
||||
package org.ryujinx.android.viewmodels
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import androidx.compose.ui.text.toLowerCase
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import com.anggrayudi.storage.SimpleStorageHelper
|
||||
import com.anggrayudi.storage.callback.FileCallback
|
||||
import com.anggrayudi.storage.file.DocumentFileCompat
|
||||
import com.anggrayudi.storage.file.DocumentFileType
|
||||
import com.anggrayudi.storage.file.copyFileTo
|
||||
import com.anggrayudi.storage.file.getAbsolutePath
|
||||
import com.google.gson.Gson
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.ryujinx.android.MainActivity
|
||||
import java.io.File
|
||||
import java.util.LinkedList
|
||||
import java.util.Queue
|
||||
import kotlin.math.max
|
||||
|
||||
class TitleUpdateViewModel(val titleId: String) {
|
||||
private var basePath: String
|
||||
private var updateJsonName = "updates.json"
|
||||
private var stagingUpdateJsonName = "staging_updates.json"
|
||||
private var storageHelper: SimpleStorageHelper
|
||||
var pathsState: SnapshotStateList<String>? = null
|
||||
|
||||
@ -56,19 +70,123 @@ class TitleUpdateViewModel(val titleId: String) {
|
||||
storageHelper.openFilePicker(UpdateRequestCode)
|
||||
}
|
||||
|
||||
fun save(index: Int) {
|
||||
fun save(
|
||||
index: Int,
|
||||
isCopying: MutableState<Boolean>,
|
||||
openDialog: MutableState<Boolean>,
|
||||
copyProgress: MutableState<Float>,
|
||||
currentProgressName: MutableState<String>
|
||||
) {
|
||||
data?.apply {
|
||||
this.selected = ""
|
||||
if(paths.isNotEmpty() && index > 0)
|
||||
{
|
||||
if (paths.isNotEmpty() && index > 0) {
|
||||
val ind = max(index - 1, paths.count() - 1)
|
||||
this.selected = paths[ind]
|
||||
}
|
||||
val gson = Gson()
|
||||
val json = gson.toJson(this)
|
||||
jsonPath = MainActivity.AppPath + "/games/" + titleId.toLowerCase(Locale.current)
|
||||
File(jsonPath).mkdirs()
|
||||
File("$jsonPath/updates.json").writeText(json)
|
||||
var json = gson.toJson(this)
|
||||
File(basePath).mkdirs()
|
||||
File("$basePath/$stagingUpdateJsonName").writeText(json)
|
||||
|
||||
// Copy updates to internal data folder
|
||||
val updatePath = "$basePath/update"
|
||||
File(updatePath).mkdirs()
|
||||
|
||||
val ioScope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
var metadata = TitleUpdateMetadata()
|
||||
var queue: Queue<String> = LinkedList()
|
||||
|
||||
var callback: FileCallback? = null
|
||||
|
||||
fun copy(path: String) {
|
||||
isCopying.value = true
|
||||
val documentFile = DocumentFileCompat.fromFullPath(
|
||||
storageHelper.storage.context,
|
||||
path,
|
||||
DocumentFileType.FILE
|
||||
)
|
||||
documentFile?.apply {
|
||||
val stagedPath = "$basePath/${name}"
|
||||
if (!File(stagedPath).exists()) {
|
||||
var file = this
|
||||
ioScope.launch {
|
||||
file.copyFileTo(
|
||||
storageHelper.storage.context,
|
||||
File(updatePath),
|
||||
callback = callback!!
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
metadata.paths.add(stagedPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun finish() {
|
||||
val savedUpdates = mutableListOf<String>()
|
||||
File(updatePath).listFiles()?.forEach { savedUpdates.add(it.absolutePath) }
|
||||
var missingFiles =
|
||||
savedUpdates.filter { i -> paths.find { it.endsWith(File(i).name) } == null }
|
||||
for (path in missingFiles) {
|
||||
File(path).delete()
|
||||
}
|
||||
|
||||
val selectedName = File(selected).name
|
||||
val newSelectedPath = "$updatePath/$selectedName"
|
||||
if (File(newSelectedPath).exists()) {
|
||||
metadata.selected = newSelectedPath
|
||||
}
|
||||
|
||||
json = gson.toJson(metadata)
|
||||
File("$basePath/$updateJsonName").writeText(json)
|
||||
|
||||
openDialog.value = false
|
||||
isCopying.value = false
|
||||
}
|
||||
callback = object : FileCallback() {
|
||||
override fun onFailed(errorCode: FileCallback.ErrorCode) {
|
||||
super.onFailed(errorCode)
|
||||
}
|
||||
|
||||
override fun onStart(file: Any, workerThread: Thread): Long {
|
||||
copyProgress.value = 0f
|
||||
|
||||
(file as DocumentFile)?.apply {
|
||||
currentProgressName.value = "Copying ${file.name}"
|
||||
}
|
||||
return super.onStart(file, workerThread)
|
||||
}
|
||||
|
||||
override fun onReport(report: Report) {
|
||||
super.onReport(report)
|
||||
|
||||
copyProgress.value = report.progress / 100f
|
||||
}
|
||||
|
||||
override fun onCompleted(result: Any) {
|
||||
super.onCompleted(result)
|
||||
|
||||
if (queue.isNotEmpty())
|
||||
copy(queue.remove())
|
||||
else {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
for (path in paths) {
|
||||
queue.add(path)
|
||||
}
|
||||
|
||||
ioScope.launch {
|
||||
if (queue.isNotEmpty()) {
|
||||
copy(queue.remove())
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,12 +202,14 @@ class TitleUpdateViewModel(val titleId: String) {
|
||||
private var jsonPath: String
|
||||
|
||||
init {
|
||||
jsonPath = MainActivity.AppPath + "/games/" + titleId.toLowerCase(Locale.current) + "/updates.json"
|
||||
basePath = MainActivity.AppPath + "/games/" + titleId.toLowerCase(Locale.current)
|
||||
val stagingJson = "${basePath}/${stagingUpdateJsonName}"
|
||||
jsonPath = "${basePath}/${updateJsonName}"
|
||||
|
||||
data = TitleUpdateMetadata()
|
||||
if (File(jsonPath).exists()) {
|
||||
if (File(stagingJson).exists()) {
|
||||
val gson = Gson()
|
||||
data = gson.fromJson(File(jsonPath).readText(), TitleUpdateMetadata::class.java)
|
||||
data = gson.fromJson(File(stagingJson).readText(), TitleUpdateMetadata::class.java)
|
||||
|
||||
data?.apply {
|
||||
val existingPaths = mutableListOf<String>()
|
||||
|
@ -11,6 +11,7 @@ import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Surface
|
||||
@ -39,7 +40,12 @@ class TitleUpdateViews {
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
|
||||
val isCopying = remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
val copyProgress = remember {
|
||||
mutableStateOf(0.0f)
|
||||
}
|
||||
Column {
|
||||
Text(text = "Updates for ${name}", textAlign = TextAlign.Center)
|
||||
Surface(
|
||||
@ -50,17 +56,19 @@ class TitleUpdateViews {
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.height(300.dp)
|
||||
.height(250.dp)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Row(modifier = Modifier.padding(8.dp)) {
|
||||
RadioButton(
|
||||
selected = (selected.value == 0),
|
||||
onClick = { selected.value = 0
|
||||
onClick = {
|
||||
selected.value = 0
|
||||
})
|
||||
Text(
|
||||
text = "None",
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.CenterVertically)
|
||||
)
|
||||
}
|
||||
@ -79,7 +87,8 @@ class TitleUpdateViews {
|
||||
onClick = { selected.value = i })
|
||||
Text(
|
||||
text = path,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.CenterVertically)
|
||||
)
|
||||
}
|
||||
@ -113,12 +122,22 @@ class TitleUpdateViews {
|
||||
}
|
||||
|
||||
}
|
||||
var currentProgressName = remember {
|
||||
mutableStateOf("Starting Copy")
|
||||
}
|
||||
if (isCopying.value) {
|
||||
Text(text = "Copying updates to local storage")
|
||||
Text(text = currentProgressName.value)
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
progress = copyProgress.value
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(18.dp))
|
||||
TextButton(
|
||||
modifier = Modifier.align(Alignment.End),
|
||||
onClick = {
|
||||
openDialog.value = false
|
||||
viewModel.save(selected.value)
|
||||
viewModel.save(selected.value, isCopying, openDialog, copyProgress, currentProgressName)
|
||||
},
|
||||
) {
|
||||
Text("Save")
|
||||
|
Loading…
x
Reference in New Issue
Block a user