release: v1.0.1 - add restore backup button and bump version

This commit is contained in:
CAtlas0784 committed 2026-10-07 23:30:40 +07:00
1 parent 7f6583df17
commit e7ca8d26af
3 files changed
+86 -5

No files matched your search

+2 -2
View File
@@ -12,8 +12,8 @@ android {
applicationId = "com.catlas.escapepenacony"
minSdk = 26
targetSdk = 35
versionCode = 1
versionName = "v1.0.0"
versionCode = 2
versionName = "v1.0.1"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -14,11 +14,12 @@ object StorageManager {
private const val PREFS_NAME = "escape_penacony_prefs"
private const val KEY_PERSISTENT_URI = "persistent_uri"
private const val KEY_SELECTED_LANG = "selected_language"
private const val KEY_PREFER_ROOT = "prefer_root_mode"
const val PPSR_PACKAGE_NAME = "com.afn.ppSR"
const val PPSR_INTERNAL_PERSISTENT_PATH =
"/data/data/com.afn.ppSR/files/server/build/build/misc/persistent.json"
const val PPSR_INTERNAL_BACKUP_PATH =
"/data/data/com.afn.ppSR/files/server/build/build/misc/persistent.json.bak"
fun savePersistentUri(context: Context, uri: Uri) {
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
@@ -101,6 +102,16 @@ object StorageManager {
val originalContent = readProcess.inputStream.bufferedReader().use { it.readText().trim() }
readProcess.waitFor()
// Save backup both inside ppSR folder and in local app cache
if (originalContent.isNotEmpty()) {
try {
val bakCmd = "cp \"$PPSR_INTERNAL_PERSISTENT_PATH\" \"$PPSR_INTERNAL_BACKUP_PATH\""
Runtime.getRuntime().exec(arrayOf("su", "-c", bakCmd)).waitFor()
val cacheBackup = File(context.cacheDir, "persistent_backup.json")
cacheBackup.writeText(originalContent, Charsets.UTF_8)
} catch (_: Exception) {}
}
val updatedJson = PersistentHelper.updateJsonString(
if (originalContent.isNotEmpty()) originalContent else "{}",
newConfig
@@ -127,9 +138,55 @@ object StorageManager {
}
}
suspend fun restoreBackup(context: Context, isRootMode: Boolean, uri: Uri?): Result<Unit> =
withContext(Dispatchers.IO) {
try {
if (isRootMode) {
// Try root backup file first
val checkCmd = "[ -f \"$PPSR_INTERNAL_BACKUP_PATH\" ] && echo 'BAK_EXISTS'"
val checkP = Runtime.getRuntime().exec(arrayOf("su", "-c", checkCmd))
val checkOut = checkP.inputStream.bufferedReader().use { it.readText().trim() }
checkP.waitFor()
if (checkOut == "BAK_EXISTS") {
val restoreCmd = "cp \"$PPSR_INTERNAL_BACKUP_PATH\" \"$PPSR_INTERNAL_PERSISTENT_PATH\" && chmod 666 \"$PPSR_INTERNAL_PERSISTENT_PATH\""
val p = Runtime.getRuntime().exec(arrayOf("su", "-c", restoreCmd))
val exit = p.waitFor()
if (exit == 0) return@withContext Result.success(Unit)
}
// Fallback to cache backup
val cacheBackup = File(context.cacheDir, "persistent_backup.json")
if (cacheBackup.exists()) {
val copyCmd = "cp \"${cacheBackup.absolutePath}\" \"$PPSR_INTERNAL_PERSISTENT_PATH\" && chmod 666 \"$PPSR_INTERNAL_PERSISTENT_PATH\""
val p = Runtime.getRuntime().exec(arrayOf("su", "-c", copyCmd))
val exit = p.waitFor()
if (exit == 0) return@withContext Result.success(Unit)
}
Result.failure(Exception("No backup file found to restore"))
} else {
// SAF fallback
if (uri == null) return@withContext Result.failure(Exception("No persistent.json selected"))
val cacheBackup = File(context.cacheDir, "persistent_backup.json")
if (!cacheBackup.exists()) return@withContext Result.failure(Exception("No backup file found"))
val backupContent = cacheBackup.readText(Charsets.UTF_8)
context.contentResolver.openOutputStream(uri, "wt")?.use { output ->
output.write(backupContent.toByteArray(Charsets.UTF_8))
output.flush()
} ?: return@withContext Result.failure(Exception("Failed to open output stream"))
Result.success(Unit)
}
} catch (e: Exception) {
e.printStackTrace()
Result.failure(e)
}
}
suspend fun restartPpsrServerViaRoot(): Result<Unit> = withContext(Dispatchers.IO) {
try {
// Force-stop then relaunch MainActivity
val restartCmd = "am force-stop $PPSR_PACKAGE_NAME && sleep 0.5 && am start -n $PPSR_PACKAGE_NAME/.MainActivity"
val process = Runtime.getRuntime().exec(arrayOf("su", "-c", restartCmd))
val exit = process.waitFor()
@@ -162,7 +219,7 @@ object StorageManager {
try {
val backupFile = File(context.cacheDir, "persistent_backup.json")
backupFile.writeText(originalContent)
backupFile.writeText(originalContent, Charsets.UTF_8)
} catch (_: Exception) {}
val updatedJson = PersistentHelper.updateJsonString(originalContent, newConfig)
@@ -303,6 +303,29 @@ fun MainScreen(
}
}
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(6.dp)) {
// Restore / Revert Backup Button
IconButton(
onClick = {
coroutineScope.launch {
val res = StorageManager.restoreBackup(context, isRootMode, currentPersistentUri)
res.onSuccess {
if (isRootMode) {
currentActiveConfig = StorageManager.readConfigViaRoot()
} else if (currentPersistentUri != null) {
currentActiveConfig = StorageManager.readCurrentConfig(context, currentPersistentUri)
}
Toast.makeText(context, "Restored backup persistent.json successfully!", Toast.LENGTH_SHORT).show()
}.onFailure { err ->
Toast.makeText(context, "Restore failed: ${err.message}", Toast.LENGTH_SHORT).show()
}
}
},
modifier = Modifier.size(34.dp)
) {
Icon(Icons.Default.Restore, contentDescription = "Restore Backup", tint = StarGold, modifier = Modifier.size(20.dp))
}
if (isRootMode) {
// Restart ppSR Server button
OutlinedButton(
@@ -343,6 +366,7 @@ fun MainScreen(
}
}
}
}
// Search Bar
OutlinedTextField(