Added ability to backup settings, databases & images

This commit is contained in:
Prathamesh More 2021-10-09 12:13:19 +05:30
parent 8ce6cf58fa
commit 897b160834
11 changed files with 495 additions and 7 deletions

View file

@ -0,0 +1,85 @@
package code.name.monkey.retromusic.activities.backup
import android.os.Bundle
import android.widget.Toast
import androidx.activity.viewModels
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsThemeActivity
import code.name.monkey.retromusic.adapter.backup.BackupAdapter
import code.name.monkey.retromusic.databinding.ActivityBackupBinding
import code.name.monkey.retromusic.helper.BackupHelper
import com.google.android.material.shape.MaterialShapeDrawable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class BackupActivity : AbsThemeActivity() {
private val backupViewModel by viewModels<BackupViewModel>()
private var backupAdapter: BackupAdapter? = null
lateinit var binding: ActivityBackupBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityBackupBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.appBarLayout.statusBarForeground =
MaterialShapeDrawable.createWithElevationOverlay(this)
binding.toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black)
initAdapter()
setupRecyclerview()
backupViewModel.backupsLiveData.observe(this) {
if (it.isNotEmpty())
backupAdapter?.swapDataset(it)
else
backupAdapter?.swapDataset(listOf())
}
backupViewModel.loadBackups()
setupButtons()
}
private fun setupButtons() {
binding.createBackup.setOnClickListener {
lifecycleScope.launch {
BackupHelper.createBackup(this@BackupActivity)
backupViewModel.loadBackups()
withContext(Dispatchers.Main) {
Toast.makeText(
this@BackupActivity,
"Backup Completed Successfully",
Toast.LENGTH_SHORT
).show()
}
}
}
}
private fun initAdapter() {
backupAdapter = BackupAdapter(this@BackupActivity, ArrayList())
backupAdapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
super.onChanged()
checkIsEmpty()
}
})
}
private fun checkIsEmpty() {
binding.emptyText.setText(R.string.no_backups_found)
(backupAdapter!!.itemCount == 0).run {
binding.empty.isVisible = this
binding.backupTitle.isVisible = this
}
}
fun setupRecyclerview() {
binding.backupRecyclerview.apply {
layoutManager = LinearLayoutManager(context)
adapter = backupAdapter
}
}
}

View file

@ -0,0 +1,23 @@
package code.name.monkey.retromusic.activities.backup
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import code.name.monkey.retromusic.helper.BackupHelper
import kotlinx.coroutines.launch
import java.io.File
class BackupViewModel : ViewModel() {
private val backupsMutableLiveData = MutableLiveData<List<File>>()
val backupsLiveData = backupsMutableLiveData
fun loadBackups() {
viewModelScope.launch {
File(BackupHelper.backupRootPath).listFiles { _, name ->
return@listFiles name.endsWith(BackupHelper.backupExt)
}?.toList()?.let {
backupsMutableLiveData.value = it
}
}
}
}

View file

@ -0,0 +1,12 @@
package code.name.monkey.retromusic.activities.backup
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import code.name.monkey.retromusic.R
class RestoreActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_restore)
}
}

View file

@ -0,0 +1,40 @@
package code.name.monkey.retromusic.adapter.backup
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.retromusic.R
import java.io.File
class BackupAdapter(
val context: Context,
var dataSet: MutableList<File>,
) : RecyclerView.Adapter<BackupAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
LayoutInflater.from(context).inflate(R.layout.item_list_card, parent, false)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.title.text = dataSet[position].nameWithoutExtension
}
override fun getItemCount(): Int = dataSet.size
fun swapDataset(dataSet: List<File>) {
this.dataSet = ArrayList(dataSet)
notifyDataSetChanged()
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val title: TextView = itemView.findViewById(R.id.title)
init {
}
}
}

View file

@ -0,0 +1,71 @@
package code.name.monkey.retromusic.helper
import android.content.Context
import android.os.Environment
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.ZipOutputStream
object BackupHelper {
suspend fun createBackup(context: Context) {
withContext(Dispatchers.IO) {
val finalPath = backupRootPath + System.currentTimeMillis().toString() + backupExt
val zipItems = mutableListOf<ZipItem>()
zipItems.addAll(getDatabaseZipItems(context))
zipItems.addAll(getSettingsZipItems(context))
getUserImageZipItems(context)?.let { zipItems.addAll(it) }
zipAll(zipItems, finalPath)
}
}
private fun zipAll(zipItems: List<ZipItem>, finalPath: String) {
ZipOutputStream(BufferedOutputStream(FileOutputStream(finalPath))).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, 1024)
}
}
}
}
}
private fun getDatabaseZipItems(context: Context): List<ZipItem> {
return context.databaseList().filter {
it.endsWith(".db")
}.map {
ZipItem(context.getDatabasePath(it).absolutePath, "databases${File.separator}$it")
}
}
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 {
ZipItem(sharedPrefPath + it, "preferences${File.separator}$it")
}
}
private fun getUserImageZipItems(context: Context): List<ZipItem>? {
return context.filesDir.listFiles { _, name ->
name.endsWith(".jpg")
}?.map {
ZipItem(it.absolutePath, "userImages${File.separator}${it.name}")
}
}
val backupRootPath =
Environment.getExternalStorageDirectory().toString() + "/RetroMusic/Backups/"
const val backupExt = ".rmbak"
private const val THEME_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]"
}
data class ZipItem(val filePath: String, val zipPath: String)