diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d1bbdd945..26baf707c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -132,9 +132,6 @@
-
-
,
val backupClickedListener: BackupClickedListener
) : RecyclerView.Adapter() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
- LayoutInflater.from(context).inflate(R.layout.item_list_card, parent, false)
+ LayoutInflater.from(activity).inflate(R.layout.item_list_card, parent, false)
)
}
@@ -34,8 +38,20 @@ class BackupAdapter(
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val title: TextView = itemView.findViewById(R.id.title)
+ val menu: AppCompatImageView = itemView.findViewById(R.id.menu)
init {
+ menu.setOnClickListener { view ->
+ val popupMenu = PopupMenu(activity, view)
+ popupMenu.inflate(R.menu.menu_backup)
+ popupMenu.setOnMenuItemClickListener { menuItem ->
+ return@setOnMenuItemClickListener backupClickedListener.onBackupMenuClicked(
+ dataSet[bindingAdapterPosition],
+ menuItem
+ )
+ }
+ popupMenu.show()
+ }
itemView.setOnClickListener {
backupClickedListener.onBackupClicked(dataSet[bindingAdapterPosition])
}
@@ -44,5 +60,7 @@ class BackupAdapter(
interface BackupClickedListener {
fun onBackupClicked(file: File)
+
+ fun onBackupMenuClicked(file: File, menuItem: MenuItem): Boolean
}
}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/backup/BackupFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/backup/BackupFragment.kt
index 9098f244b..ead8c5c24 100644
--- a/app/src/main/java/code/name/monkey/retromusic/fragments/backup/BackupFragment.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/fragments/backup/BackupFragment.kt
@@ -1,7 +1,11 @@
package code.name.monkey.retromusic.fragments.backup
+import android.content.Intent
import android.os.Bundle
+import android.view.MenuItem
import android.view.View
+import android.widget.Toast
+import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
@@ -12,6 +16,9 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.backup.BackupAdapter
import code.name.monkey.retromusic.databinding.FragmentBackupBinding
import code.name.monkey.retromusic.helper.BackupHelper
+import code.name.monkey.retromusic.util.BackupUtil
+import com.afollestad.materialdialogs.MaterialDialog
+import com.afollestad.materialdialogs.input.input
import kotlinx.coroutines.launch
import java.io.File
@@ -40,15 +47,25 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
private fun setupButtons() {
binding.createBackup.setOnClickListener {
- lifecycleScope.launch {
- BackupHelper.createBackup(requireContext())
- backupViewModel.loadBackups()
+ MaterialDialog(requireContext()).show {
+ title(res = R.string.action_rename)
+ input(prefill = System.currentTimeMillis().toString()) { _, text ->
+ // Text submitted with the action button
+ lifecycleScope.launch {
+ BackupHelper.createBackup(requireContext(), text.toString())
+ backupViewModel.loadBackups()
+ }
+ }
+ positiveButton(R.string.action_rename)
+ negativeButton(R.string.action_cancel)
+ setTitle(R.string.action_rename)
}
+
}
}
private fun initAdapter() {
- backupAdapter = BackupAdapter(requireContext(), ArrayList(), this)
+ backupAdapter = BackupAdapter(requireActivity(), ArrayList(), this)
backupAdapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
super.onChanged()
@@ -72,8 +89,64 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
}
override fun onBackupClicked(file: File) {
- lifecycleScope.launch {
- backupViewModel.restoreBackup(requireActivity(), file)
+ AlertDialog.Builder(requireContext())
+ .setTitle(R.string.restore)
+ .setMessage(R.string.restore_message)
+ .setPositiveButton(R.string.restore) { _, _ ->
+ lifecycleScope.launch {
+ backupViewModel.restoreBackup(requireActivity(), file)
+ }
+ }
+ .setNegativeButton(android.R.string.cancel, null)
+ .create()
+ .show()
+ }
+
+ override fun onBackupMenuClicked(file: File, menuItem: MenuItem): Boolean {
+ when (menuItem.itemId) {
+ R.id.action_delete -> {
+ try {
+ file.delete()
+ } catch (exception: SecurityException) {
+ Toast.makeText(
+ activity,
+ "Could not delete backup",
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ backupViewModel.loadBackups()
+ return true
+ }
+ R.id.action_share -> {
+ activity?.startActivity(
+ Intent.createChooser(BackupUtil.createShareFileIntent(file, requireContext()), null))
+ return true
+ }
+ R.id.action_rename -> {
+ MaterialDialog(requireContext()).show {
+ title(res = R.string.action_rename)
+ input(prefill = file.nameWithoutExtension) { _, text ->
+ // Text submitted with the action button
+ val renamedFile =
+ File(file.parent + File.separator + text + BackupHelper.APPEND_EXTENSION)
+ if (!renamedFile.exists()) {
+ file.renameTo(renamedFile)
+ backupViewModel.loadBackups()
+ } else {
+ Toast.makeText(
+ requireContext(),
+ "File already exists",
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ }
+ positiveButton(R.string.action_rename)
+ negativeButton(R.string.action_cancel)
+ setTitle(R.string.action_rename)
+ }
+ return true
+ }
}
+ return false
}
}
\ No newline at end of file
diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/BackupHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/BackupHelper.kt
index 82f18a22c..018cb5075 100644
--- a/app/src/main/java/code/name/monkey/retromusic/helper/BackupHelper.kt
+++ b/app/src/main/java/code/name/monkey/retromusic/helper/BackupHelper.kt
@@ -3,6 +3,7 @@ package code.name.monkey.retromusic.helper
import android.content.Context
import android.os.Environment
import android.widget.Toast
+import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.BuildConfig
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@@ -12,29 +13,36 @@ import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream
object BackupHelper {
- suspend fun createBackup(context: Context) {
+ suspend fun createBackup(context: Context, name: String) {
withContext(Dispatchers.IO) {
- val finalPath =
- backupRootPath + System.currentTimeMillis().toString() + APPEND_EXTENSION
+ val backupFile =
+ File(backupRootPath + name + APPEND_EXTENSION)
+ if (backupFile.parentFile?.exists() != true) {
+ backupFile.parentFile?.mkdirs()
+ }
val zipItems = mutableListOf()
zipItems.addAll(getDatabaseZipItems(context))
zipItems.addAll(getSettingsZipItems(context))
getUserImageZipItems(context)?.let { zipItems.addAll(it) }
- zipAll(zipItems, finalPath)
+ zipAll(zipItems, backupFile)
}
}
- private fun zipAll(zipItems: List, 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 zipAll(zipItems: List, backupFile: File) {
+ try {
+ 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, 1024)
+ }
}
}
}
+ } catch (exception: FileNotFoundException) {
+ Toast.makeText(App.getContext(), "Couldn't create backup", Toast.LENGTH_SHORT).show()
}
}
@@ -124,7 +132,7 @@ object BackupHelper {
val backupRootPath =
Environment.getExternalStorageDirectory().toString() + "/RetroMusic/Backups/"
const val BACKUP_EXTENSION = "rmbak"
- private const val APPEND_EXTENSION = ".$BACKUP_EXTENSION"
+ const val APPEND_EXTENSION = ".$BACKUP_EXTENSION"
private const val DATABASES_PATH = "databases"
private const val SETTINGS_PATH = "prefs"
private const val IMAGES_PATH = "userImages"
diff --git a/app/src/main/java/code/name/monkey/retromusic/util/BackupUtil.kt b/app/src/main/java/code/name/monkey/retromusic/util/BackupUtil.kt
new file mode 100644
index 000000000..c3f00af0f
--- /dev/null
+++ b/app/src/main/java/code/name/monkey/retromusic/util/BackupUtil.kt
@@ -0,0 +1,30 @@
+package code.name.monkey.retromusic.util
+
+import android.content.Context
+import android.content.Intent
+import android.widget.Toast
+import androidx.core.content.FileProvider
+import java.io.File
+
+object BackupUtil {
+ fun createShareFileIntent(file: File, context: Context): Intent? {
+ return try {
+ Intent().setAction(Intent.ACTION_SEND).putExtra(
+ Intent.EXTRA_STREAM,
+ FileProvider.getUriForFile(
+ context,
+ context.applicationContext.packageName,
+ file
+ )
+ ).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION).setType("*/*")
+ } catch (e: IllegalArgumentException) {
+ e.printStackTrace()
+ Toast.makeText(
+ context,
+ "Could not share this file.",
+ Toast.LENGTH_SHORT
+ ).show()
+ Intent()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_backup.xml b/app/src/main/res/menu/menu_backup.xml
new file mode 100644
index 000000000..760eab793
--- /dev/null
+++ b/app/src/main/res/menu/menu_backup.xml
@@ -0,0 +1,19 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index be470180a..2486f8f90 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -514,4 +514,5 @@
You have to select at least one category.
You will be forwarded to the issue tracker website.
Your account data is only used for authentication.
+ Do you want to restore backup?