diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ddc9040..94e2e43 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -70,7 +70,6 @@ dependencies { type = "aar" } } - implementation(libs.lz4.java) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/app/src/main/java/eu/m724/pojavbackup/core/backup/BackupWorker.kt b/app/src/main/java/eu/m724/pojavbackup/core/backup/BackupWorker.kt index f73383b..da7f06f 100644 --- a/app/src/main/java/eu/m724/pojavbackup/core/backup/BackupWorker.kt +++ b/app/src/main/java/eu/m724/pojavbackup/core/backup/BackupWorker.kt @@ -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)) } } \ No newline at end of file diff --git a/app/src/main/java/eu/m724/pojavbackup/core/backup/PathTools.kt b/app/src/main/java/eu/m724/pojavbackup/core/backup/PathTools.kt index c62e8f7..16928f1 100644 --- a/app/src/main/java/eu/m724/pojavbackup/core/backup/PathTools.kt +++ b/app/src/main/java/eu/m724/pojavbackup/core/backup/PathTools.kt @@ -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() diff --git a/app/src/main/java/eu/m724/pojavbackup/settings/screen/options/OptionsScreen.kt b/app/src/main/java/eu/m724/pojavbackup/settings/screen/options/OptionsScreen.kt index 2bb5069..6d0eb2d 100644 --- a/app/src/main/java/eu/m724/pojavbackup/settings/screen/options/OptionsScreen.kt +++ b/app/src/main/java/eu/m724/pojavbackup/settings/screen/options/OptionsScreen.kt @@ -46,7 +46,8 @@ fun OptionsScreen( Text("Backup now") } - Text(uiState.backupStatus) + Text(uiState.backupStatusHeader) + Text(uiState.backupStatusDetails) } } diff --git a/app/src/main/java/eu/m724/pojavbackup/settings/screen/options/OptionsScreenUiState.kt b/app/src/main/java/eu/m724/pojavbackup/settings/screen/options/OptionsScreenUiState.kt index e8f97f7..0573a3b 100644 --- a/app/src/main/java/eu/m724/pojavbackup/settings/screen/options/OptionsScreenUiState.kt +++ b/app/src/main/java/eu/m724/pojavbackup/settings/screen/options/OptionsScreenUiState.kt @@ -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 = "" ) diff --git a/app/src/main/java/eu/m724/pojavbackup/settings/screen/options/OptionsScreenViewModel.kt b/app/src/main/java/eu/m724/pojavbackup/settings/screen/options/OptionsScreenViewModel.kt index 392ea6b..7a7ce21 100644 --- a/app/src/main/java/eu/m724/pojavbackup/settings/screen/options/OptionsScreenViewModel.kt +++ b/app/src/main/java/eu/m724/pojavbackup/settings/screen/options/OptionsScreenViewModel.kt @@ -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) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ceaccdc..088fc5e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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" }