This commit is contained in:
Minecon724 2025-04-25 11:16:05 +02:00
commit baac9e7fce
Signed by: Minecon724
GPG key ID: A02E6E67AB961189
7 changed files with 55 additions and 38 deletions

View file

@ -70,7 +70,6 @@ dependencies {
type = "aar"
}
}
implementation(libs.lz4.java)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)

View file

@ -1,7 +1,6 @@
package eu.m724.pojavbackup.core.backup
import android.content.Context
import android.util.Log
import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile
import androidx.hilt.work.HiltWorker
@ -13,8 +12,10 @@ import dagger.assisted.AssistedInject
import eu.m724.pojavbackup.core.backup.Backup.BackupStatus
import eu.m724.pojavbackup.core.data.GameDataRepository
import eu.m724.pojavbackup.core.datastore.SettingsRepository
import org.apache.commons.compress.compressors.CompressorStreamFactory
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.util.UUID
@HiltWorker
class BackupWorker @AssistedInject constructor(
@ -24,12 +25,8 @@ class BackupWorker @AssistedInject constructor(
private val backupRepository: BackupRepository,
private val gameDataRepository: GameDataRepository
) : CoroutineWorker(appContext, workerParams) {
companion object {
const val TAG = "BackupWorker"
}
override suspend fun doWork(): Result {
statusUpdate("Creating")
setProgress(statusData(UUID(0, 0), "Initializing"))
val backup = backupRepository.createBackup()
@ -42,29 +39,28 @@ class BackupWorker @AssistedInject constructor(
.createDirectory(DateTimeFormatter.ISO_LOCAL_DATE.format(LocalDate.now()) + " (" + backup.id + ")")!!
return try {
statusUpdate("#${backup.id} Initialized")
Log.d(TAG, "Initialized backup: ${backup.id}")
worldIds.forEach {
statusUpdate("#${backup.id} Backing up world $it")
Log.d(TAG, "Backing up world $it")
setProgress(statusData(backup.id, "$it: now backing up"))
val world = runCatching {
gameDataRepository.getWorld(it)
}
if (world.isFailure) {
Log.e(TAG, "Cannot backup world $it:" + world.exceptionOrNull())
Exception("Error backing up world $it", world.exceptionOrNull()).printStackTrace()
setProgress(statusData(backup.id, "$it: error"))
return@forEach
}
val documentFile = backupDirectory.createFile("application/zstd", "$it.tar.zst")!!
val compression = if (settings.improvement) CompressorStreamFactory.ZSTANDARD else CompressorStreamFactory.GZIP
val documentFile = backupDirectory.createFile("application/zstd", "$it.tar.$compression")!!
applicationContext.contentResolver.openOutputStream(documentFile.uri)!!.use {
PathTools.compressDocumentFileDirectory(
contentResolver = applicationContext.contentResolver,
source = world.getOrThrow().documentFile!!,
target = it,
compression = compression,
inflate = settings.improvement
)
}
@ -72,21 +68,24 @@ class BackupWorker @AssistedInject constructor(
backupRepository.completeBackup(backup, BackupStatus.SUCCESS)
statusUpdate("#${backup.id} Done")
Result.success()
Result.success(statusData(backup.id, "Success"))
} catch (e: Exception) {
e.printStackTrace()
statusUpdate("#${backup.id} Error: $e")
Exception("Error backing up", e).printStackTrace()
backupRepository.completeBackup(backup, BackupStatus.FAILURE)
Result.failure()
Result.failure(statusData(backup.id, "Error: $e"))
}
}
// TODO only for test
suspend fun statusUpdate(status: String) {
setProgress(Data.Builder().putString("status", status).build())
fun statusData(uuid: UUID, status: String): Data {
return Data.Builder()
.putUuid("uuid", uuid)
.putString("status", status)
.build()
}
fun Data.Builder.putUuid(key: String, value: UUID): Data.Builder {
return this.putLongArray(key, longArrayOf(value.mostSignificantBits, value.leastSignificantBits))
}
}

View file

@ -4,7 +4,7 @@ import android.content.ContentResolver
import androidx.documentfile.provider.DocumentFile
import org.apache.commons.compress.archivers.tar.TarArchiveEntry
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream
import org.apache.commons.compress.compressors.zstandard.ZstdCompressorOutputStream
import org.apache.commons.compress.compressors.CompressorStreamFactory
import java.io.ByteArrayOutputStream
import java.io.OutputStream
@ -14,10 +14,11 @@ class PathTools {
contentResolver: ContentResolver,
source: DocumentFile,
target: OutputStream,
inflate: Boolean = false
compression: String,
inflate: Boolean = false,
) {
target.use {
ZstdCompressorOutputStream(it).use {
CompressorStreamFactory().createCompressorOutputStream(compression, it).use {
TarArchiveOutputStream(it).use { outputStream ->
compressInner(contentResolver, source, outputStream, "", inflate)
outputStream.finish()

View file

@ -46,7 +46,8 @@ fun OptionsScreen(
Text("Backup now")
}
Text(uiState.backupStatus)
Text(uiState.backupStatusHeader)
Text(uiState.backupStatusDetails)
}
}

View file

@ -2,5 +2,6 @@ package eu.m724.pojavbackup.settings.screen.options
data class OptionsScreenUiState(
val improvement: Boolean = false,
val backupStatus: String = ""
val backupStatusHeader: String = "",
val backupStatusDetails: String = ""
)

View file

@ -29,9 +29,23 @@ class OptionsScreenViewModel @Inject constructor(
private val workManager = WorkManager
.getInstance(appContext)
init {
viewModelScope.launch {
settingsRepository.getSettingsFlow().collect { settings ->
_uiState.update {
it.copy(
improvement = settings.improvement
)
}
}
}
}
fun backupNow() {
_uiState.update {
it.copy(backupStatus = "Starting")
it.copy(
backupStatusHeader = "Starting"
)
}
val uuid = UUID.randomUUID()
@ -45,9 +59,19 @@ class OptionsScreenViewModel @Inject constructor(
viewModelScope.launch {
workManager.getWorkInfoByIdFlow(uuid).collect { workInfo ->
val data = if (workInfo?.state?.isFinished == true) workInfo.outputData else workInfo?.progress
val ub = data?.getLongArray("uuid")
var uuid: UUID? = null
if (ub != null) {
uuid = UUID(ub[0], ub[1])
}
_uiState.update {
it.copy(
backupStatus = workInfo?.state.toString() + workInfo?.progress?.getString("status")
backupStatusHeader = workInfo?.state?.toString() + " " + data?.getString("status").toString(),
backupStatusDetails = uuid.toString()
)
}
}
@ -57,12 +81,6 @@ class OptionsScreenViewModel @Inject constructor(
fun setOptions(
improvement: Boolean? = true
) {
_uiState.update {
it.copy(
improvement = improvement ?: it.improvement
)
}
viewModelScope.launch {
settingsRepository.updateSettings(improvement = improvement)
}

View file

@ -24,7 +24,6 @@ commonsCompress = "1.27.1"
work = "2.10.0"
androidxHilt = "1.2.0"
zstdJni = "1.5.7-2"
lz4Java = "1.8.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@ -57,7 +56,6 @@ androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx"
androidx-hilt-compiler = { group = "androidx.hilt", name = "hilt-compiler", version.ref = "androidxHilt" }
hilt-work = { group = "androidx.hilt", name = "hilt-work", version.ref = "androidxHilt"}
zstd-jni = { group = "com.github.luben", name = "zstd-jni", version.ref = "zstdJni" }
lz4-java = { group = "org.lz4", name = "lz4-java", version.ref = "lz4Java" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }