Added ability to restore

This commit is contained in:
Prathamesh More 2021-10-09 13:29:14 +05:30
parent 897b160834
commit d703a05182
4 changed files with 120 additions and 8 deletions

View file

@ -16,8 +16,9 @@ import com.google.android.material.shape.MaterialShapeDrawable
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File
class BackupActivity : AbsThemeActivity() { class BackupActivity : AbsThemeActivity(), BackupAdapter.BackupClickedListener {
private val backupViewModel by viewModels<BackupViewModel>() private val backupViewModel by viewModels<BackupViewModel>()
private var backupAdapter: BackupAdapter? = null private var backupAdapter: BackupAdapter? = null
@ -59,7 +60,7 @@ class BackupActivity : AbsThemeActivity() {
} }
private fun initAdapter() { private fun initAdapter() {
backupAdapter = BackupAdapter(this@BackupActivity, ArrayList()) backupAdapter = BackupAdapter(this@BackupActivity, ArrayList(), this)
backupAdapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { backupAdapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onChanged() { override fun onChanged() {
super.onChanged() super.onChanged()
@ -82,4 +83,10 @@ class BackupActivity : AbsThemeActivity() {
adapter = backupAdapter adapter = backupAdapter
} }
} }
override fun onBackupClicked(file: File) {
lifecycleScope.launch {
backupViewModel.restoreBackup(this@BackupActivity, file)
}
}
} }

View file

@ -1,11 +1,17 @@
package code.name.monkey.retromusic.activities.backup package code.name.monkey.retromusic.activities.backup
import android.app.Activity
import android.content.Intent
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import code.name.monkey.retromusic.helper.BackupHelper import code.name.monkey.retromusic.helper.BackupHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import kotlin.system.exitProcess
class BackupViewModel : ViewModel() { class BackupViewModel : ViewModel() {
private val backupsMutableLiveData = MutableLiveData<List<File>>() private val backupsMutableLiveData = MutableLiveData<List<File>>()
@ -14,10 +20,22 @@ class BackupViewModel : ViewModel() {
fun loadBackups() { fun loadBackups() {
viewModelScope.launch { viewModelScope.launch {
File(BackupHelper.backupRootPath).listFiles { _, name -> File(BackupHelper.backupRootPath).listFiles { _, name ->
return@listFiles name.endsWith(BackupHelper.backupExt) return@listFiles name.endsWith(BackupHelper.BACKUP_EXTENSION)
}?.toList()?.let { }?.toList()?.let {
backupsMutableLiveData.value = it backupsMutableLiveData.value = it
} }
} }
} }
suspend fun restoreBackup(activity: Activity, file: File) {
BackupHelper.restoreBackup(activity, file)
withContext(Dispatchers.Main) {
val intent = Intent(
activity,
activity::class.java
)
activity.startActivity(intent)
exitProcess(0)
}
}
} }

View file

@ -12,6 +12,7 @@ import java.io.File
class BackupAdapter( class BackupAdapter(
val context: Context, val context: Context,
var dataSet: MutableList<File>, var dataSet: MutableList<File>,
val backupClickedListener: BackupClickedListener
) : RecyclerView.Adapter<BackupAdapter.ViewHolder>() { ) : RecyclerView.Adapter<BackupAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
@ -35,6 +36,13 @@ class BackupAdapter(
val title: TextView = itemView.findViewById(R.id.title) val title: TextView = itemView.findViewById(R.id.title)
init { init {
itemView.setOnClickListener {
backupClickedListener.onBackupClicked(dataSet[bindingAdapterPosition])
}
} }
} }
interface BackupClickedListener {
fun onBackupClicked(file: File)
}
} }

View file

@ -2,17 +2,20 @@ package code.name.monkey.retromusic.helper
import android.content.Context import android.content.Context
import android.os.Environment import android.os.Environment
import android.widget.Toast
import code.name.monkey.retromusic.BuildConfig import code.name.monkey.retromusic.BuildConfig
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.* import java.io.*
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream import java.util.zip.ZipOutputStream
object BackupHelper { object BackupHelper {
suspend fun createBackup(context: Context) { suspend fun createBackup(context: Context) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val finalPath = backupRootPath + System.currentTimeMillis().toString() + backupExt val finalPath =
backupRootPath + System.currentTimeMillis().toString() + APPEND_EXTENSION
val zipItems = mutableListOf<ZipItem>() val zipItems = mutableListOf<ZipItem>()
zipItems.addAll(getDatabaseZipItems(context)) zipItems.addAll(getDatabaseZipItems(context))
zipItems.addAll(getSettingsZipItems(context)) zipItems.addAll(getSettingsZipItems(context))
@ -39,7 +42,7 @@ object BackupHelper {
return context.databaseList().filter { return context.databaseList().filter {
it.endsWith(".db") it.endsWith(".db")
}.map { }.map {
ZipItem(context.getDatabasePath(it).absolutePath, "databases${File.separator}$it") ZipItem(context.getDatabasePath(it).absolutePath, "$DATABASES_PATH${File.separator}$it")
} }
} }
@ -49,7 +52,7 @@ object BackupHelper {
"${BuildConfig.APPLICATION_ID}_preferences.xml", // App settings pref path "${BuildConfig.APPLICATION_ID}_preferences.xml", // App settings pref path
"$THEME_PREFS_KEY_DEFAULT.xml" // appthemehelper pref path "$THEME_PREFS_KEY_DEFAULT.xml" // appthemehelper pref path
).map { ).map {
ZipItem(sharedPrefPath + it, "preferences${File.separator}$it") ZipItem(sharedPrefPath + it, "$SETTINGS_PATH${File.separator}$it")
} }
} }
@ -57,15 +60,91 @@ object BackupHelper {
return context.filesDir.listFiles { _, name -> return context.filesDir.listFiles { _, name ->
name.endsWith(".jpg") name.endsWith(".jpg")
}?.map { }?.map {
ZipItem(it.absolutePath, "userImages${File.separator}${it.name}") ZipItem(it.absolutePath, "$IMAGES_PATH${File.separator}${it.name}")
}
}
suspend fun restoreBackup(context: Context, file: File) {
withContext(Dispatchers.IO) {
ZipInputStream(FileInputStream(file)).use {
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)
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)
}
} }
} }
val backupRootPath = val backupRootPath =
Environment.getExternalStorageDirectory().toString() + "/RetroMusic/Backups/" Environment.getExternalStorageDirectory().toString() + "/RetroMusic/Backups/"
const val backupExt = ".rmbak" const val BACKUP_EXTENSION = "zip"
private const val APPEND_EXTENSION = ".$BACKUP_EXTENSION"
private const val DATABASES_PATH = "databases"
private const val SETTINGS_PATH = "prefs"
private const val IMAGES_PATH = "userImages"
private const val THEME_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]" private const val THEME_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]"
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)
}
private fun ZipEntry.getFileName(): String {
return name.substring(name.lastIndexOf(File.separator))
}
} }
data class ZipItem(val filePath: String, val zipPath: String) data class ZipItem(val filePath: String, val zipPath: String)