create a copy of updates when added

This commit is contained in:
Emmanuel Hansen 2023-08-13 19:22:00 +00:00
parent 7a2fadb499
commit 091d9d4546
2 changed files with 160 additions and 21 deletions

View File

@ -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>()

View File

@ -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")