Skip to content

Commit

Permalink
feat: Save config after backing up
Browse files Browse the repository at this point in the history
* For reloading

Change-Id: If61e17e6d5c7225623a67197b7c79079f123b3af
  • Loading branch information
XayahSuSuSu committed Sep 29, 2023
1 parent 5a80688 commit d670e75
Show file tree
Hide file tree
Showing 13 changed files with 211 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.xayah.databackup.util.binPath
import com.xayah.databackup.util.extendPath
import com.xayah.databackup.util.filesPath
import com.xayah.databackup.util.readMonetEnabled
import com.xayah.librootservice.service.RemoteRootService
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
Expand Down Expand Up @@ -55,5 +56,7 @@ class DataBackupApplication : Application() {
super.onCreate()
application = this
monetEnabled = mutableStateOf(readMonetEnabled())
// Kill daemon
RemoteRootService(this).destroyService(killDaemon = true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ data class PackageRestoreEntire(
var active: Boolean,
) {
@Ignore
@Transient
var selected: MutableState<Boolean> = mutableStateOf(false)

val isSystemApp: Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import com.xayah.databackup.data.PackageRestoreOperation
import com.xayah.databackup.data.PackageRestoreOperationDao
import com.xayah.databackup.util.DataType
import com.xayah.databackup.util.DateUtil
import com.xayah.databackup.util.GsonUtil
import com.xayah.databackup.util.LogUtil
import com.xayah.databackup.util.PathUtil
import com.xayah.databackup.util.command.OperationBackupUtil
import com.xayah.databackup.util.command.OperationRestoreUtil
import com.xayah.databackup.util.command.PreparationUtil
import com.xayah.databackup.util.databasePath
import com.xayah.databackup.util.iconPath
import com.xayah.databackup.util.readBackupItself
import com.xayah.databackup.util.readBackupUserId
Expand Down Expand Up @@ -67,6 +67,9 @@ class OperationLocalServiceImpl : Service() {
@Inject
lateinit var packageRestoreOperationDao: PackageRestoreOperationDao

@Inject
lateinit var gsonUtil: GsonUtil

suspend fun backupPackagesPreparation(): BackupPreparation = withIOContext {
mutex.withLock {
val logTag = "Packages backup preparation"
Expand All @@ -90,7 +93,7 @@ class OperationLocalServiceImpl : Service() {
val logTag = "Packages backup"
val context = applicationContext
val remoteRootService = RemoteRootService(context)
val operationBackupUtil = OperationBackupUtil(context, timestamp, logUtil, remoteRootService, packageBackupOperationDao)
val operationBackupUtil = OperationBackupUtil(context, timestamp, logUtil, remoteRootService, packageBackupOperationDao, gsonUtil)

logUtil.log(logTag, "Started.")
val packages = packageBackupEntireDao.queryActiveTotalPackages()
Expand Down Expand Up @@ -163,6 +166,9 @@ class OperationLocalServiceImpl : Service() {
)
packageRestoreEntireDao.upsert(restoreEntire)

// Save config
operationBackupUtil.backupConfig(restoreEntire, DataType.PACKAGE_CONFIG)

// Reset selected items if enabled.
if (context.readResetBackupList()) {
currentPackage.operationCode = OperationMask.None
Expand Down Expand Up @@ -218,7 +224,7 @@ class OperationLocalServiceImpl : Service() {
}
}

// Backup database and icons.
// Backup icons.
val iconPath = context.iconPath()
val iconSavePath = PathUtil.getIconSavePath()
remoteRootService.copyRecursively(path = iconPath, targetPath = iconSavePath, overwrite = true)
Expand All @@ -228,11 +234,6 @@ class OperationLocalServiceImpl : Service() {
remoteRootService.createNewFile(path = noMediaSavePath)
logUtil.log(logTag, "Created $noMediaSavePath.")

val databasePath = context.databasePath()
val databaseSavePath = PathUtil.getDatabaseSavePath()
remoteRootService.copyRecursively(path = databasePath, targetPath = databaseSavePath, overwrite = true)
logUtil.log(logTag, "Copied from $databasePath to $databaseSavePath.")

remoteRootService.destroyService()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.hilt.navigation.compose.hiltViewModel
import com.google.gson.reflect.TypeToken
import com.xayah.databackup.R
import com.xayah.databackup.data.OperationMask
import com.xayah.databackup.data.PackageRestoreEntire
Expand All @@ -54,12 +55,14 @@ import com.xayah.databackup.ui.token.RadioTokens
import com.xayah.databackup.util.CompressionType
import com.xayah.databackup.util.ConstantUtil
import com.xayah.databackup.util.DataType
import com.xayah.databackup.util.GsonUtil
import com.xayah.databackup.util.IntentUtil
import com.xayah.databackup.util.LogUtil
import com.xayah.databackup.util.PathUtil
import com.xayah.databackup.util.command.EnvUtil
import com.xayah.databackup.util.command.InstallationUtil
import com.xayah.databackup.util.command.PreparationUtil
import com.xayah.databackup.util.iconPath
import com.xayah.databackup.util.readExternalRestoreSaveChild
import com.xayah.databackup.util.readInternalRestoreSaveChild
import com.xayah.databackup.util.readRestoreSavePath
Expand Down Expand Up @@ -211,7 +214,7 @@ private data class TypedPath(
)

@ExperimentalMaterial3Api
private suspend fun DialogState.openReloadDialog(context: Context, logUtil: LogUtil, packageRestoreEntireDao: PackageRestoreEntireDao) {
private suspend fun DialogState.openReloadDialog(context: Context, logUtil: LogUtil, packageRestoreEntireDao: PackageRestoreEntireDao, gsonUtil: GsonUtil) {
openLoading(
title = context.getString(R.string.prompt),
icon = ImageVector.vectorResource(context.theme, context.resources, R.drawable.ic_rounded_folder_open),
Expand All @@ -228,8 +231,9 @@ private suspend fun DialogState.openReloadDialog(context: Context, logUtil: LogU
logUtil.log(logTag, "Clear the table and try to create icon dir")
// Clear table first
packageRestoreEntireDao.clearTable()
// Create icon dir if it doesn't exist
// Create icon dir if it doesn't exist, then copy icons from backup dir.
EnvUtil.createIconDirectory(context)
remoteRootService.copyRecursively(path = PathUtil.getRestoreIconSavePath(), targetPath = context.iconPath(), overwrite = true)

logUtil.log(logTag, "Classify the paths")
// Classify the paths
Expand Down Expand Up @@ -276,8 +280,12 @@ private suspend fun DialogState.openReloadDialog(context: Context, logUtil: LogU

logUtil.log(logTag, "Timestamp: $timestamp")
val tmpApkPath = PathUtil.getTmpApkPath(context = context, packageName = packageName)
val tmpConfigPath = PathUtil.getTmpConfigPath(context = context, packageName = packageName, timestamp = timestamp)
val tmpConfigFilePath = PathUtil.getTmpConfigFilePath(context = context, packageName = packageName, timestamp = timestamp)
remoteRootService.deleteRecursively(tmpApkPath)
remoteRootService.deleteRecursively(tmpConfigPath)
remoteRootService.mkdirs(tmpApkPath)
remoteRootService.mkdirs(tmpConfigPath)

archivePathList.forEach { archivePath ->
// For each archive
Expand Down Expand Up @@ -305,6 +313,20 @@ private suspend fun DialogState.openReloadDialog(context: Context, logUtil: LogU
}
}

DataType.PACKAGE_CONFIG.type -> {
val type = CompressionType.suffixOf(archivePath.extension)
if (type != null) {
compressionType = type
installationUtil.decompress(
archivePath = archivePath.pathString,
tmpApkPath = tmpConfigPath,
compressionType = type
)
} else {
logUtil.log(logTag, "Failed to parse compression type: ${archivePath.extension}")
}
}

DataType.PACKAGE_USER.type -> {
operationCode = operationCode or OperationMask.Data
}
Expand All @@ -316,32 +338,42 @@ private suspend fun DialogState.openReloadDialog(context: Context, logUtil: LogU
)
}

val packageRestoreEntire = PackageRestoreEntire(
packageName = packageName,
label = "",
backupOpCode = operationCode,
operationCode = OperationMask.None,
timestamp = timestamp,
versionName = "",
versionCode = 0,
flags = 0,
compressionType = compressionType,
active = false
)
packageInfo?.apply {
packageRestoreEntire.also { entity ->
entity.label = applicationInfo.loadLabel(packageManager).toString()
entity.versionName = versionName ?: ""
entity.versionCode = longVersionCode
entity.flags = applicationInfo.flags
// Check config first
val packageRestoreEntire: PackageRestoreEntire
if (remoteRootService.exists(tmpConfigFilePath)) {
// Directly read from config
val json = remoteRootService.readText(tmpConfigFilePath)
val type = object : TypeToken<PackageRestoreEntire>() {}.type
packageRestoreEntire = gsonUtil.fromJson(json, type)
} else {
packageRestoreEntire = PackageRestoreEntire(
packageName = packageName,
label = "",
backupOpCode = operationCode,
operationCode = OperationMask.None,
timestamp = timestamp,
versionName = "",
versionCode = 0,
flags = 0,
compressionType = compressionType,
active = false
)
packageInfo?.apply {
packageRestoreEntire.also { entity ->
entity.label = applicationInfo.loadLabel(packageManager).toString()
entity.versionName = versionName ?: ""
entity.versionCode = longVersionCode
entity.flags = applicationInfo.flags
}
val icon = applicationInfo.loadIcon(packageManager)
EnvUtil.saveIcon(context, packageName, icon)
logUtil.log(logTag, "Icon saved")
}
val icon = applicationInfo.loadIcon(packageManager)
EnvUtil.saveIcon(context, packageName, icon)
logUtil.log(logTag, "Icon saved")
}
packageRestoreEntireDao.upsert(packageRestoreEntire)

remoteRootService.deleteRecursively(tmpApkPath)
remoteRootService.deleteRecursively(tmpConfigPath)
}
}
remoteRootService.destroyService()
Expand Down Expand Up @@ -387,7 +419,7 @@ fun PageRestore() {
val onClicks = listOf<suspend () -> Unit>(
{
dialogSlot.openConfirmDialog(context, context.getString(R.string.confirm_reload)).also { (confirmed, _) ->
if (confirmed) dialogSlot.openReloadDialog(context, uiState.logUtil, uiState.packageRestoreEntireDao)
if (confirmed) dialogSlot.openReloadDialog(context, uiState.logUtil, uiState.packageRestoreEntireDao, uiState.gsonUtil)
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import com.xayah.databackup.data.PackageRestoreEntireDao
import com.xayah.databackup.util.GsonUtil
import com.xayah.databackup.util.LogUtil
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

data class RestoreUiState(
val logUtil: LogUtil,
val packageRestoreEntireDao: PackageRestoreEntireDao,
val gsonUtil: GsonUtil,
)

@HiltViewModel
class RestoreViewModel @Inject constructor(logUtil: LogUtil, packageRestoreEntireDao: PackageRestoreEntireDao) : ViewModel() {
private val _uiState = mutableStateOf(RestoreUiState(logUtil = logUtil, packageRestoreEntireDao = packageRestoreEntireDao))
class RestoreViewModel @Inject constructor(logUtil: LogUtil, packageRestoreEntireDao: PackageRestoreEntireDao, gsonUtil: GsonUtil) : ViewModel() {
private val _uiState = mutableStateOf(RestoreUiState(logUtil = logUtil, packageRestoreEntireDao = packageRestoreEntireDao, gsonUtil = gsonUtil))
val uiState: State<RestoreUiState>
get() = _uiState
}
17 changes: 17 additions & 0 deletions source/app/src/main/java/com/xayah/databackup/util/GsonUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.xayah.databackup.util

import com.google.gson.Gson
import java.lang.reflect.Type
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class GsonUtil @Inject constructor() {
private val gson = Gson()

fun toJson(src: Any): String = gson.toJson(src)

fun <T> fromJson(json: String, type: Type): T = run {
gson.fromJson(json, type)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ fun Context.extendPath(): String = "${filesPath()}/extend"

fun Context.iconPath(): String = "${filesPath()}/icon"

fun Context.databasePath(): String = "${filesDir.parent}/databases"

@SuppressLint("SdCardPath")
object PathUtil {
fun getParentPath(path: String): String = Paths.get(path).parent.pathString
Expand All @@ -32,6 +30,10 @@ object PathUtil {

fun getTmpApkPath(context: Context, packageName: String): String = "${context.filesPath()}/tmp/apks/$packageName"

fun getTmpConfigPath(context: Context, packageName: String, timestamp: Long): String = "${context.filesPath()}/tmp/config/$packageName/$timestamp"
fun getTmpConfigFilePath(context: Context, packageName: String, timestamp: Long): String =
"${getTmpConfigPath(context, packageName, timestamp)}/PackageRestoreEntire"

// Paths for backup save dir.
fun getBackupSavePath(): String = DataBackupApplication.application.readBackupSavePath()
private fun getBackupArchivesSavePath(): String = "${getBackupSavePath()}/archives"
Expand All @@ -40,14 +42,14 @@ object PathUtil {
fun getTreeSavePath(timestamp: Long): String = "${getTreeSavePath()}/tree_${timestamp}"
fun getIconSavePath(): String = "${getBackupSavePath()}/icon"
fun getIconNoMediaSavePath(): String = "${getIconSavePath()}/.nomedia"
fun getDatabaseSavePath(): String = "${getBackupSavePath()}/databases"
private fun getLogSavePath(): String = "${getBackupSavePath()}/log"
fun getLogSavePath(timestamp: Long): String = "${getLogSavePath()}/log_${timestamp}"

// Paths for restore save dir.
private fun getRestoreSavePath(): String = DataBackupApplication.application.readRestoreSavePath()
private fun getRestoreArchivesSavePath(): String = "${getRestoreSavePath()}/archives"
fun getRestorePackagesSavePath(): String = "${getRestoreArchivesSavePath()}/packages"
fun getRestoreIconSavePath(): String = "${getRestoreSavePath()}/icon"

// Paths for processing.
fun getPackageUserPath(userId: Int): String = "/data/user/${userId}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ enum class DataType(val type: String) {
PACKAGE_DATA("data"),
PACKAGE_OBB("obb"),
PACKAGE_MEDIA("media"), // /data/media/$user_id/Android/media
PACKAGE_CONFIG("config"), // Json file for reloading
MEDIA_MEDIA("media");

fun origin(userId: Int): String = when (this) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,39 @@ object CompressionUtil {
return Pair(isSuccess, outList.toLineString().trim())
}

suspend fun compress(
logUtil: LogUtil,
logId: Long,
compatibleMode: Boolean,
compressionType: CompressionType,
archivePath: String,
originPath: String,
): Pair<Boolean, String> {
var isSuccess = true
val outList = mutableListOf<String>()

val cmd = if (compatibleMode)
"- ./* ${if (compressionType == CompressionType.TAR) "" else "| ${compressionType.compressPara}"} > $archivePath"
else
"$archivePath ./* ${if (compressionType == CompressionType.TAR) "" else "-I $QUOTE${compressionType.compressPara}$QUOTE"}"

// Compress config dir.
logUtil.executeWithLog(logId, "cd $originPath").also { result ->
if (result.isSuccess.not()) isSuccess = false
outList.add(result.outString())
}
logUtil.executeWithLog(logId, "tar --totals -cpf $cmd").also { result ->
if (result.isSuccess.not()) isSuccess = false
outList.add(result.outString())
}
logUtil.executeWithLog(logId, "cd /").also { result ->
if (result.isSuccess.not()) isSuccess = false
outList.add(result.outString())
}

return Pair(isSuccess, outList.toLineString().trim())
}

suspend fun decompress(
logUtil: LogUtil,
logId: Long,
Expand Down
Loading

0 comments on commit d670e75

Please sign in to comment.