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.launch
import kotlinx.coroutines.withContext
import java.io.File
class BackupActivity : AbsThemeActivity() {
class BackupActivity : AbsThemeActivity(), BackupAdapter.BackupClickedListener {
private val backupViewModel by viewModels<BackupViewModel>()
private var backupAdapter: BackupAdapter? = null
@ -59,7 +60,7 @@ class BackupActivity : AbsThemeActivity() {
}
private fun initAdapter() {
backupAdapter = BackupAdapter(this@BackupActivity, ArrayList())
backupAdapter = BackupAdapter(this@BackupActivity, ArrayList(), this)
backupAdapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
super.onChanged()
@ -82,4 +83,10 @@ class BackupActivity : AbsThemeActivity() {
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
import android.app.Activity
import android.content.Intent
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import code.name.monkey.retromusic.helper.BackupHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import kotlin.system.exitProcess
class BackupViewModel : ViewModel() {
private val backupsMutableLiveData = MutableLiveData<List<File>>()
@ -14,10 +20,22 @@ class BackupViewModel : ViewModel() {
fun loadBackups() {
viewModelScope.launch {
File(BackupHelper.backupRootPath).listFiles { _, name ->
return@listFiles name.endsWith(BackupHelper.backupExt)
return@listFiles name.endsWith(BackupHelper.BACKUP_EXTENSION)
}?.toList()?.let {
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(
val context: Context,
var dataSet: MutableList<File>,
val backupClickedListener: BackupClickedListener
) : RecyclerView.Adapter<BackupAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
@ -35,6 +36,13 @@ class BackupAdapter(
val title: TextView = itemView.findViewById(R.id.title)
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.os.Environment
import android.widget.Toast
import code.name.monkey.retromusic.BuildConfig
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.*
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream
object BackupHelper {
suspend fun createBackup(context: Context) {
withContext(Dispatchers.IO) {
val finalPath = backupRootPath + System.currentTimeMillis().toString() + backupExt
val finalPath =
backupRootPath + System.currentTimeMillis().toString() + APPEND_EXTENSION
val zipItems = mutableListOf<ZipItem>()
zipItems.addAll(getDatabaseZipItems(context))
zipItems.addAll(getSettingsZipItems(context))
@ -39,7 +42,7 @@ object BackupHelper {
return context.databaseList().filter {
it.endsWith(".db")
}.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
"$THEME_PREFS_KEY_DEFAULT.xml" // appthemehelper pref path
).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 ->
name.endsWith(".jpg")
}?.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 =
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 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)