2021-10-09 06:43:19 +00:00
|
|
|
package code.name.monkey.retromusic.helper
|
|
|
|
|
|
|
|
import android.content.Context
|
|
|
|
import android.os.Environment
|
2021-10-09 07:59:14 +00:00
|
|
|
import android.widget.Toast
|
2021-10-23 16:01:33 +00:00
|
|
|
import code.name.monkey.retromusic.App
|
2021-10-09 06:43:19 +00:00
|
|
|
import code.name.monkey.retromusic.BuildConfig
|
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
|
import kotlinx.coroutines.withContext
|
|
|
|
import java.io.*
|
|
|
|
import java.util.zip.ZipEntry
|
2021-10-09 07:59:14 +00:00
|
|
|
import java.util.zip.ZipInputStream
|
2021-10-09 06:43:19 +00:00
|
|
|
import java.util.zip.ZipOutputStream
|
|
|
|
|
|
|
|
object BackupHelper {
|
2021-10-23 16:01:33 +00:00
|
|
|
suspend fun createBackup(context: Context, name: String) {
|
2021-11-19 12:14:07 +00:00
|
|
|
val backupFile =
|
2021-11-23 19:07:16 +00:00
|
|
|
File(backupRootPath + File.separator + name + APPEND_EXTENSION)
|
2021-11-19 12:14:07 +00:00
|
|
|
if (backupFile.parentFile?.exists() != true) {
|
|
|
|
backupFile.parentFile?.mkdirs()
|
|
|
|
}
|
|
|
|
val zipItems = mutableListOf<ZipItem>()
|
|
|
|
zipItems.addAll(getDatabaseZipItems(context))
|
|
|
|
zipItems.addAll(getSettingsZipItems(context))
|
|
|
|
getUserImageZipItems(context)?.let { zipItems.addAll(it) }
|
2021-11-27 14:37:22 +00:00
|
|
|
zipItems.addAll(getCustomArtistZipItems(context))
|
2021-11-27 08:35:31 +00:00
|
|
|
zipAll(zipItems, backupFile)
|
2021-10-09 06:43:19 +00:00
|
|
|
}
|
|
|
|
|
2021-11-23 19:07:16 +00:00
|
|
|
private suspend fun zipAll(zipItems: List<ZipItem>, backupFile: File) {
|
2021-11-27 08:35:31 +00:00
|
|
|
withContext(Dispatchers.IO) {
|
|
|
|
kotlin.runCatching {
|
|
|
|
ZipOutputStream(BufferedOutputStream(FileOutputStream(backupFile))).use { out ->
|
|
|
|
for (zipItem in zipItems) {
|
|
|
|
FileInputStream(zipItem.filePath).use { fi ->
|
|
|
|
BufferedInputStream(fi).use { origin ->
|
|
|
|
val entry = ZipEntry(zipItem.zipPath)
|
|
|
|
out.putNextEntry(entry)
|
|
|
|
origin.copyTo(out)
|
|
|
|
}
|
2021-10-23 16:01:33 +00:00
|
|
|
}
|
2021-10-09 06:43:19 +00:00
|
|
|
}
|
|
|
|
}
|
2021-11-27 08:35:31 +00:00
|
|
|
}.onFailure {
|
|
|
|
it.printStackTrace()
|
|
|
|
withContext(Dispatchers.Main) {
|
|
|
|
Toast.makeText(App.getContext(), "Couldn't create backup", Toast.LENGTH_SHORT)
|
|
|
|
.show()
|
|
|
|
}
|
2021-10-09 06:43:19 +00:00
|
|
|
}
|
2021-11-23 19:07:16 +00:00
|
|
|
withContext(Dispatchers.Main) {
|
2021-11-27 08:35:31 +00:00
|
|
|
Toast.makeText(App.getContext(), "Backup created successfully", Toast.LENGTH_SHORT)
|
2021-11-23 19:07:16 +00:00
|
|
|
.show()
|
|
|
|
}
|
2021-10-09 06:43:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun getDatabaseZipItems(context: Context): List<ZipItem> {
|
|
|
|
return context.databaseList().filter {
|
|
|
|
it.endsWith(".db")
|
|
|
|
}.map {
|
2021-10-09 07:59:14 +00:00
|
|
|
ZipItem(context.getDatabasePath(it).absolutePath, "$DATABASES_PATH${File.separator}$it")
|
2021-10-09 06:43:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun getSettingsZipItems(context: Context): List<ZipItem> {
|
|
|
|
val sharedPrefPath = context.filesDir.parentFile?.absolutePath + "/shared_prefs/"
|
|
|
|
return listOf(
|
|
|
|
"${BuildConfig.APPLICATION_ID}_preferences.xml", // App settings pref path
|
|
|
|
"$THEME_PREFS_KEY_DEFAULT.xml" // appthemehelper pref path
|
|
|
|
).map {
|
2021-10-09 07:59:14 +00:00
|
|
|
ZipItem(sharedPrefPath + it, "$SETTINGS_PATH${File.separator}$it")
|
2021-10-09 06:43:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun getUserImageZipItems(context: Context): List<ZipItem>? {
|
|
|
|
return context.filesDir.listFiles { _, name ->
|
|
|
|
name.endsWith(".jpg")
|
|
|
|
}?.map {
|
2021-10-09 07:59:14 +00:00
|
|
|
ZipItem(it.absolutePath, "$IMAGES_PATH${File.separator}${it.name}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-27 14:37:22 +00:00
|
|
|
private fun getCustomArtistZipItems(context: Context): List<ZipItem> {
|
|
|
|
val zipItemList = mutableListOf<ZipItem>()
|
|
|
|
val sharedPrefPath = context.filesDir.parentFile?.absolutePath + "/shared_prefs/"
|
|
|
|
|
|
|
|
zipItemList.addAll(
|
|
|
|
File(context.filesDir, "custom_artist_images")
|
|
|
|
.listFiles()?.map {
|
|
|
|
ZipItem(
|
|
|
|
it.absolutePath,
|
|
|
|
"$CUSTOM_ARTISTS_PATH${File.separator}custom_artist_images${File.separator}${it.name}"
|
|
|
|
)
|
|
|
|
}?.toList() ?: listOf()
|
|
|
|
)
|
|
|
|
zipItemList.add(
|
|
|
|
ZipItem(
|
|
|
|
sharedPrefPath + File.separator + "custom_artist_image.xml",
|
|
|
|
"$CUSTOM_ARTISTS_PATH${File.separator}prefs${File.separator}custom_artist_image.xml"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return zipItemList
|
|
|
|
}
|
|
|
|
|
2021-11-23 19:07:16 +00:00
|
|
|
suspend fun restoreBackup(context: Context, inputStream: InputStream?) {
|
2021-10-09 07:59:14 +00:00
|
|
|
withContext(Dispatchers.IO) {
|
2021-11-23 19:07:16 +00:00
|
|
|
ZipInputStream(inputStream).use {
|
2021-10-09 07:59:14 +00:00
|
|
|
var entry = it.nextEntry
|
|
|
|
while (entry != null) {
|
|
|
|
if (entry.isDatabaseEntry()) restoreDatabase(context, it, entry)
|
|
|
|
if (entry.isPreferenceEntry()) restorePreferences(context, it, entry)
|
|
|
|
if (entry.isImageEntry()) restoreImages(context, it, entry)
|
2021-11-27 14:37:22 +00:00
|
|
|
if (entry.isCustomArtistImageEntry()) restoreCustomArtistImages(
|
|
|
|
context,
|
|
|
|
it,
|
|
|
|
entry
|
|
|
|
)
|
|
|
|
if (entry.isCustomArtistPrefEntry()) restoreCustomArtistPrefs(
|
|
|
|
context,
|
|
|
|
it,
|
|
|
|
entry
|
|
|
|
)
|
2021-10-09 07:59:14 +00:00
|
|
|
entry = it.nextEntry
|
|
|
|
}
|
|
|
|
}
|
|
|
|
withContext(Dispatchers.Main) {
|
|
|
|
Toast.makeText(context, "Restore Completed Successfully", Toast.LENGTH_SHORT).show()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun restoreImages(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) {
|
|
|
|
val filePath =
|
|
|
|
context.filesDir.path + File.separator + zipEntry.getFileName()
|
|
|
|
BufferedOutputStream(FileOutputStream(filePath)).use { bos ->
|
|
|
|
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE)
|
|
|
|
var read: Int
|
|
|
|
while (zipIn.read(bytesIn).also { read = it } != -1) {
|
|
|
|
bos.write(bytesIn, 0, read)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun restorePreferences(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) {
|
|
|
|
val file = File(
|
|
|
|
context.filesDir.parent!! + File.separator + "shared_prefs" + File.separator + zipEntry.getFileName()
|
|
|
|
)
|
|
|
|
if (file.exists()) {
|
|
|
|
file.delete()
|
|
|
|
}
|
|
|
|
BufferedOutputStream(FileOutputStream(file)).use { bos ->
|
|
|
|
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE)
|
|
|
|
var read: Int
|
|
|
|
while (zipIn.read(bytesIn).also { read = it } != -1) {
|
|
|
|
bos.write(bytesIn, 0, read)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun restoreDatabase(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) {
|
|
|
|
val filePath =
|
|
|
|
context.filesDir.parent!! + File.separator + DATABASES_PATH + File.separator + zipEntry.getFileName()
|
|
|
|
BufferedOutputStream(FileOutputStream(filePath)).use { bos ->
|
|
|
|
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE)
|
|
|
|
var read: Int
|
|
|
|
while (zipIn.read(bytesIn).also { read = it } != -1) {
|
|
|
|
bos.write(bytesIn, 0, read)
|
|
|
|
}
|
2021-10-09 06:43:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-27 14:37:22 +00:00
|
|
|
private fun restoreCustomArtistImages(
|
|
|
|
context: Context,
|
|
|
|
zipIn: ZipInputStream,
|
|
|
|
zipEntry: ZipEntry
|
|
|
|
) {
|
|
|
|
val parentFolder = File(context.filesDir, "custom_artist_images")
|
|
|
|
|
|
|
|
if (!parentFolder.exists()) {
|
|
|
|
parentFolder.mkdirs()
|
|
|
|
}
|
|
|
|
BufferedOutputStream(
|
|
|
|
FileOutputStream(
|
|
|
|
File(
|
|
|
|
parentFolder,
|
|
|
|
zipEntry.getFileName()
|
|
|
|
)
|
|
|
|
)
|
|
|
|
).use { bos ->
|
|
|
|
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE)
|
|
|
|
var read: Int
|
|
|
|
while (zipIn.read(bytesIn).also { read = it } != -1) {
|
|
|
|
bos.write(bytesIn, 0, read)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun restoreCustomArtistPrefs(
|
|
|
|
context: Context,
|
|
|
|
zipIn: ZipInputStream,
|
|
|
|
zipEntry: ZipEntry
|
|
|
|
) {
|
|
|
|
val filePath =
|
|
|
|
context.filesDir.parentFile?.absolutePath + "/shared_prefs/" + zipEntry.getFileName()
|
|
|
|
BufferedOutputStream(FileOutputStream(filePath)).use { bos ->
|
|
|
|
val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE)
|
|
|
|
var read: Int
|
|
|
|
while (zipIn.read(bytesIn).also { read = it } != -1) {
|
|
|
|
bos.write(bytesIn, 0, read)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-09 06:43:19 +00:00
|
|
|
val backupRootPath =
|
2021-11-19 12:14:07 +00:00
|
|
|
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
|
|
|
|
.toString() + "/RetroMusic/Backups/"
|
2021-10-11 06:17:12 +00:00
|
|
|
const val BACKUP_EXTENSION = "rmbak"
|
2021-10-23 16:01:33 +00:00
|
|
|
const val APPEND_EXTENSION = ".$BACKUP_EXTENSION"
|
2021-10-09 07:59:14 +00:00
|
|
|
private const val DATABASES_PATH = "databases"
|
|
|
|
private const val SETTINGS_PATH = "prefs"
|
|
|
|
private const val IMAGES_PATH = "userImages"
|
2021-11-27 14:37:22 +00:00
|
|
|
private const val CUSTOM_ARTISTS_PATH = "artistImages"
|
2021-10-09 06:43:19 +00:00
|
|
|
private const val THEME_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]"
|
|
|
|
|
2021-10-09 07:59:14 +00:00
|
|
|
private fun ZipEntry.isDatabaseEntry(): Boolean {
|
|
|
|
return name.startsWith(DATABASES_PATH)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun ZipEntry.isPreferenceEntry(): Boolean {
|
|
|
|
return name.startsWith(SETTINGS_PATH)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun ZipEntry.isImageEntry(): Boolean {
|
|
|
|
return name.startsWith(IMAGES_PATH)
|
|
|
|
}
|
|
|
|
|
2021-11-27 14:37:22 +00:00
|
|
|
private fun ZipEntry.isCustomArtistImageEntry(): Boolean {
|
|
|
|
return name.startsWith(CUSTOM_ARTISTS_PATH) && name.contains("custom_artist_images")
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun ZipEntry.isCustomArtistPrefEntry(): Boolean {
|
|
|
|
return name.startsWith(CUSTOM_ARTISTS_PATH) && name.contains("prefs")
|
|
|
|
}
|
|
|
|
|
2021-10-09 07:59:14 +00:00
|
|
|
private fun ZipEntry.getFileName(): String {
|
|
|
|
return name.substring(name.lastIndexOf(File.separator))
|
|
|
|
}
|
2021-10-09 06:43:19 +00:00
|
|
|
}
|
|
|
|
|
2021-11-23 19:07:16 +00:00
|
|
|
data class ZipItem(val filePath: String, val zipPath: String)
|
|
|
|
|
|
|
|
fun CharSequence.sanitize(): String {
|
|
|
|
return toString().replace("/", "_")
|
|
|
|
.replace(":", "_")
|
|
|
|
.replace("*", "_")
|
|
|
|
.replace("?", "_")
|
|
|
|
.replace("\"", "_")
|
|
|
|
.replace("<", "_")
|
|
|
|
.replace(">", "_")
|
|
|
|
.replace("|", "_")
|
|
|
|
.replace("\\", "_")
|
|
|
|
.replace("&", "_")
|
|
|
|
}
|