From db3a7d409700a8695e0bd8dde59d5c0540c0a120 Mon Sep 17 00:00:00 2001 From: Prathamesh More Date: Sat, 23 Oct 2021 21:31:33 +0530 Subject: [PATCH] Fixed crash and added more options to backup --- app/src/main/AndroidManifest.xml | 4 - .../adapter/backup/BackupAdapter.kt | 24 +++++- .../fragments/backup/BackupFragment.kt | 85 +++++++++++++++++-- .../monkey/retromusic/helper/BackupHelper.kt | 34 +++++--- .../name/monkey/retromusic/util/BackupUtil.kt | 30 +++++++ app/src/main/res/menu/menu_backup.xml | 19 +++++ app/src/main/res/values/strings.xml | 1 + 7 files changed, 171 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/code/name/monkey/retromusic/util/BackupUtil.kt create mode 100644 app/src/main/res/menu/menu_backup.xml 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?