Added Chooser to choose what to restore
This commit is contained in:
parent
2e16994276
commit
5a41a07b76
9 changed files with 315 additions and 61 deletions
|
@ -123,15 +123,38 @@
|
||||||
<activity android:name=".activities.LockScreenActivity" />
|
<activity android:name=".activities.LockScreenActivity" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".fragments.backup.RestoreActivity"
|
android:name=".fragments.backup.RestoreActivity"
|
||||||
android:exported="true">
|
android:excludeFromRecents="false"
|
||||||
|
android:exported="true"
|
||||||
|
android:label="@string/restore"
|
||||||
|
android:theme="@style/Theme.RetroMusic.Dialog">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:mimeType="application/octet-stream" />
|
||||||
|
<data android:mimeType="application/x-zip-compressed" />
|
||||||
|
<data android:mimeType="application/zip" />
|
||||||
|
|
||||||
<data android:scheme="file" />
|
<data android:scheme="file" />
|
||||||
|
<data android:scheme="content" />
|
||||||
<data android:mimeType="*/*" />
|
<data android:mimeType="*/*" />
|
||||||
|
<!--
|
||||||
|
Work around Android's ugly primitive PatternMatcher
|
||||||
|
implementation that can't cope with finding a . early in
|
||||||
|
the path unless it's explicitly matched.
|
||||||
|
-->
|
||||||
|
<data android:host="*" />
|
||||||
<data android:pathPattern=".*\\.rmbak" />
|
<data android:pathPattern=".*\\.rmbak" />
|
||||||
|
<data android:pathPattern=".*\\..*\\.rmbak" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\.rmbak" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\.rmbak" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.rmbak" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.rmbak" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
@ -273,10 +296,9 @@
|
||||||
<service
|
<service
|
||||||
android:name=".service.MusicService"
|
android:name=".service.MusicService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="true"
|
android:exported="false"
|
||||||
android:foregroundServiceType="mediaPlayback"
|
android:foregroundServiceType="mediaPlayback"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name">
|
||||||
tools:ignore="ExportedService">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.media.browse.MediaBrowserService" />
|
<action android:name="android.media.browse.MediaBrowserService" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
|
@ -7,7 +7,7 @@ import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.core.net.toUri
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
|
@ -49,7 +49,9 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
|
||||||
val openFilePicker = registerForActivityResult(ActivityResultContracts.OpenDocument()) {
|
val openFilePicker = registerForActivityResult(ActivityResultContracts.OpenDocument()) {
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
it?.let {
|
it?.let {
|
||||||
backupViewModel.restoreBackup(requireActivity(), requireContext().contentResolver.openInputStream(it))
|
startActivity(Intent(context, RestoreActivity::class.java).apply {
|
||||||
|
data = it
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,17 +105,11 @@ class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupC
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackupClicked(file: File) {
|
override fun onBackupClicked(file: File) {
|
||||||
AlertDialog.Builder(requireContext())
|
lifecycleScope.launch {
|
||||||
.setTitle(R.string.restore)
|
startActivity(Intent(context, RestoreActivity::class.java).apply {
|
||||||
.setMessage(R.string.restore_message)
|
data = file.toUri()
|
||||||
.setPositiveButton(R.string.restore) { _, _ ->
|
})
|
||||||
lifecycleScope.launch {
|
}
|
||||||
backupViewModel.restoreBackup(requireActivity(), file.inputStream())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
|
||||||
.create()
|
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("CheckResult")
|
@SuppressLint("CheckResult")
|
||||||
|
|
|
@ -5,6 +5,8 @@ import android.content.Intent
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import code.name.monkey.retromusic.activities.MainActivity
|
||||||
|
import code.name.monkey.retromusic.helper.BackupContent
|
||||||
import code.name.monkey.retromusic.helper.BackupHelper
|
import code.name.monkey.retromusic.helper.BackupHelper
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
@ -25,12 +27,12 @@ class BackupViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun restoreBackup(activity: Activity, inputStream: InputStream?) {
|
suspend fun restoreBackup(activity: Activity, inputStream: InputStream?, contents: List<BackupContent>) {
|
||||||
BackupHelper.restoreBackup(activity, inputStream)
|
BackupHelper.restoreBackup(activity, inputStream, contents)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
val intent = Intent(
|
val intent = Intent(
|
||||||
activity,
|
activity,
|
||||||
activity::class.java
|
MainActivity::class.java
|
||||||
)
|
)
|
||||||
activity.startActivity(intent)
|
activity.startActivity(intent)
|
||||||
exitProcess(0)
|
exitProcess(0)
|
||||||
|
|
|
@ -1,12 +1,89 @@
|
||||||
package code.name.monkey.retromusic.fragments.backup
|
package code.name.monkey.retromusic.fragments.backup
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.provider.MediaStore
|
||||||
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import code.name.monkey.retromusic.R
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import code.name.monkey.retromusic.databinding.ActivityRestoreBinding
|
||||||
|
import code.name.monkey.retromusic.helper.BackupContent
|
||||||
|
import code.name.monkey.retromusic.helper.BackupContent.*
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
|
import code.name.monkey.retromusic.util.theme.ThemeManager
|
||||||
|
import com.google.android.material.color.DynamicColors
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
class RestoreActivity : AppCompatActivity() {
|
class RestoreActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
lateinit var binding: ActivityRestoreBinding
|
||||||
|
private val backupViewModel: BackupViewModel by viewModels()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
updateTheme()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_restore)
|
binding = ActivityRestoreBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
val backupUri = intent?.data
|
||||||
|
binding.backupName.setText(getFileName(backupUri))
|
||||||
|
binding.cancelButton.setOnClickListener {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
binding.restoreButton.setOnClickListener {
|
||||||
|
val backupContents = mutableListOf<BackupContent>()
|
||||||
|
if (binding.checkSettings.isChecked) backupContents.add(SETTINGS)
|
||||||
|
if (binding.checkQueue.isChecked) backupContents.add(QUEUE)
|
||||||
|
if (binding.checkDatabases.isChecked) backupContents.add(PLAYLISTS)
|
||||||
|
if (binding.checkArtistImages.isChecked) backupContents.add(CUSTOM_ARTIST_IMAGES)
|
||||||
|
if (binding.checkUserImages.isChecked) backupContents.add(USER_IMAGES)
|
||||||
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
if (backupUri != null) {
|
||||||
|
contentResolver.openInputStream(backupUri)?.use {
|
||||||
|
backupViewModel.restoreBackup(this@RestoreActivity, it, backupContents)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateTheme() {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(ThemeManager.getNightMode(this))
|
||||||
|
|
||||||
|
// Apply dynamic colors to activity if enabled
|
||||||
|
if (PreferenceUtil.materialYou) {
|
||||||
|
DynamicColors.applyIfAvailable(
|
||||||
|
this,
|
||||||
|
com.google.android.material.R.style.ThemeOverlay_Material3_DynamicColors_DayNight
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFileName(uri: Uri?): String? {
|
||||||
|
when (uri?.scheme) {
|
||||||
|
"file" -> {
|
||||||
|
return uri.lastPathSegment
|
||||||
|
}
|
||||||
|
"content" -> {
|
||||||
|
val proj = arrayOf(MediaStore.Images.Media.TITLE)
|
||||||
|
contentResolver.query(
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
|
||||||
|
MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
|
||||||
|
} else {
|
||||||
|
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
|
||||||
|
}, proj, null, null, null
|
||||||
|
)?.use { cursor ->
|
||||||
|
if (cursor.count != 0) {
|
||||||
|
val columnIndex: Int =
|
||||||
|
cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE)
|
||||||
|
cursor.moveToFirst()
|
||||||
|
return cursor.getString(columnIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "Backup"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,9 +2,13 @@ package code.name.monkey.retromusic.helper
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
import code.name.monkey.retromusic.App
|
import code.name.monkey.retromusic.App
|
||||||
import code.name.monkey.retromusic.BuildConfig
|
import code.name.monkey.retromusic.BuildConfig
|
||||||
|
import code.name.monkey.retromusic.helper.BackupContent.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.*
|
import java.io.*
|
||||||
|
@ -24,10 +28,11 @@ object BackupHelper {
|
||||||
zipItems.addAll(getSettingsZipItems(context))
|
zipItems.addAll(getSettingsZipItems(context))
|
||||||
getUserImageZipItems(context)?.let { zipItems.addAll(it) }
|
getUserImageZipItems(context)?.let { zipItems.addAll(it) }
|
||||||
zipItems.addAll(getCustomArtistZipItems(context))
|
zipItems.addAll(getCustomArtistZipItems(context))
|
||||||
|
zipItems.addAll(getQueueZipItems(context))
|
||||||
zipAll(zipItems, backupFile)
|
zipAll(zipItems, backupFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun zipAll(zipItems: List<ZipItem>, backupFile: File) {
|
private suspend fun zipAll(zipItems: List<ZipItem>, backupFile: File) =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
ZipOutputStream(BufferedOutputStream(FileOutputStream(backupFile))).use { out ->
|
ZipOutputStream(BufferedOutputStream(FileOutputStream(backupFile))).use { out ->
|
||||||
|
@ -42,27 +47,42 @@ object BackupHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
it.printStackTrace()
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
Toast.makeText(App.getContext(), "Couldn't create backup", Toast.LENGTH_SHORT)
|
Toast.makeText(App.getContext(), "Couldn't create backup", Toast.LENGTH_SHORT)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
throw Exception(it)
|
||||||
|
}.onSuccess {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
Toast.makeText(
|
||||||
|
App.getContext(),
|
||||||
|
"Backup created successfully",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
Toast.makeText(App.getContext(), "Backup created successfully", Toast.LENGTH_SHORT)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun getDatabaseZipItems(context: Context): List<ZipItem> {
|
private fun getDatabaseZipItems(context: Context): List<ZipItem> {
|
||||||
return context.databaseList().filter {
|
return context.databaseList().filter {
|
||||||
it.endsWith(".db")
|
it.endsWith(".db") && it != queueDatabase
|
||||||
}.map {
|
}.map {
|
||||||
ZipItem(context.getDatabasePath(it).absolutePath, "$DATABASES_PATH${File.separator}$it")
|
ZipItem(context.getDatabasePath(it).absolutePath, "$DATABASES_PATH${File.separator}$it")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getQueueZipItems(context: Context): List<ZipItem> {
|
||||||
|
Log.d("RetroMusic", context.getDatabasePath(queueDatabase).absolutePath)
|
||||||
|
return listOf(
|
||||||
|
ZipItem(
|
||||||
|
context.getDatabasePath(queueDatabase).absolutePath,
|
||||||
|
"$QUEUE_PATH${File.separator}$queueDatabase"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun getSettingsZipItems(context: Context): List<ZipItem> {
|
private fun getSettingsZipItems(context: Context): List<ZipItem> {
|
||||||
val sharedPrefPath = context.filesDir.parentFile?.absolutePath + "/shared_prefs/"
|
val sharedPrefPath = context.filesDir.parentFile?.absolutePath + "/shared_prefs/"
|
||||||
return listOf(
|
return listOf(
|
||||||
|
@ -94,33 +114,47 @@ object BackupHelper {
|
||||||
)
|
)
|
||||||
}?.toList() ?: listOf()
|
}?.toList() ?: listOf()
|
||||||
)
|
)
|
||||||
zipItemList.add(
|
File(sharedPrefPath + File.separator + "custom_artist_image.xml").let {
|
||||||
ZipItem(
|
if (it.exists()) {
|
||||||
sharedPrefPath + File.separator + "custom_artist_image.xml",
|
zipItemList.add(
|
||||||
"$CUSTOM_ARTISTS_PATH${File.separator}prefs${File.separator}custom_artist_image.xml"
|
ZipItem(
|
||||||
)
|
it.absolutePath,
|
||||||
)
|
"$CUSTOM_ARTISTS_PATH${File.separator}prefs${File.separator}custom_artist_image.xml"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return zipItemList
|
return zipItemList
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun restoreBackup(context: Context, inputStream: InputStream?) {
|
suspend fun restoreBackup(
|
||||||
|
context: Context,
|
||||||
|
inputStream: InputStream?,
|
||||||
|
contents: List<BackupContent>
|
||||||
|
) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
ZipInputStream(inputStream).use {
|
ZipInputStream(inputStream).use {
|
||||||
var entry = it.nextEntry
|
var entry = it.nextEntry
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
if (entry.isDatabaseEntry()) restoreDatabase(context, it, entry)
|
if (entry.isDatabaseEntry() && contents.contains(PLAYLISTS)) {
|
||||||
if (entry.isPreferenceEntry()) restorePreferences(context, it, entry)
|
restoreDatabase(context, it, entry)
|
||||||
if (entry.isImageEntry()) restoreImages(context, it, entry)
|
} else if (entry.isPreferenceEntry() && contents.contains(SETTINGS)) {
|
||||||
if (entry.isCustomArtistImageEntry()) restoreCustomArtistImages(
|
restorePreferences(context, it, entry)
|
||||||
context,
|
} else if (entry.isImageEntry() && contents.contains(USER_IMAGES)) {
|
||||||
it,
|
restoreImages(context, it, entry)
|
||||||
entry
|
|
||||||
)
|
} else if (entry.isCustomArtistImageEntry() && contents.contains(
|
||||||
if (entry.isCustomArtistPrefEntry()) restoreCustomArtistPrefs(
|
CUSTOM_ARTIST_IMAGES
|
||||||
context,
|
)
|
||||||
it,
|
) {
|
||||||
entry
|
restoreCustomArtistImages(context, it, entry)
|
||||||
)
|
restoreCustomArtistPrefs(context, it, entry)
|
||||||
|
} else if (entry.isQueueEntry() && contents.contains(QUEUE)) {
|
||||||
|
restoreQueueDatabase(context, it, entry)
|
||||||
|
}
|
||||||
|
|
||||||
entry = it.nextEntry
|
entry = it.nextEntry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,6 +204,21 @@ object BackupHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun restoreQueueDatabase(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(context).edit(commit = true) {
|
||||||
|
putInt("POSITION", 0)
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun restoreCustomArtistImages(
|
private fun restoreCustomArtistImages(
|
||||||
context: Context,
|
context: Context,
|
||||||
zipIn: ZipInputStream,
|
zipIn: ZipInputStream,
|
||||||
|
@ -218,10 +267,12 @@ object BackupHelper {
|
||||||
const val BACKUP_EXTENSION = "rmbak"
|
const val BACKUP_EXTENSION = "rmbak"
|
||||||
const val APPEND_EXTENSION = ".$BACKUP_EXTENSION"
|
const val APPEND_EXTENSION = ".$BACKUP_EXTENSION"
|
||||||
private const val DATABASES_PATH = "databases"
|
private const val DATABASES_PATH = "databases"
|
||||||
|
private const val QUEUE_PATH = "queue"
|
||||||
private const val SETTINGS_PATH = "prefs"
|
private const val SETTINGS_PATH = "prefs"
|
||||||
private const val IMAGES_PATH = "userImages"
|
private const val IMAGES_PATH = "userImages"
|
||||||
private const val CUSTOM_ARTISTS_PATH = "artistImages"
|
private const val CUSTOM_ARTISTS_PATH = "artistImages"
|
||||||
private const val THEME_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]"
|
private const val THEME_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]"
|
||||||
|
private const val queueDatabase = "music_playback_state.db"
|
||||||
|
|
||||||
private fun ZipEntry.isDatabaseEntry(): Boolean {
|
private fun ZipEntry.isDatabaseEntry(): Boolean {
|
||||||
return name.startsWith(DATABASES_PATH)
|
return name.startsWith(DATABASES_PATH)
|
||||||
|
@ -243,6 +294,10 @@ object BackupHelper {
|
||||||
return name.startsWith(CUSTOM_ARTISTS_PATH) && name.contains("prefs")
|
return name.startsWith(CUSTOM_ARTISTS_PATH) && name.contains("prefs")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun ZipEntry.isQueueEntry(): Boolean {
|
||||||
|
return name.startsWith(QUEUE_PATH)
|
||||||
|
}
|
||||||
|
|
||||||
private fun ZipEntry.getFileName(): String {
|
private fun ZipEntry.getFileName(): String {
|
||||||
return name.substring(name.lastIndexOf(File.separator))
|
return name.substring(name.lastIndexOf(File.separator))
|
||||||
}
|
}
|
||||||
|
@ -262,3 +317,11 @@ fun CharSequence.sanitize(): String {
|
||||||
.replace("\\", "_")
|
.replace("\\", "_")
|
||||||
.replace("&", "_")
|
.replace("&", "_")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class BackupContent {
|
||||||
|
SETTINGS,
|
||||||
|
USER_IMAGES,
|
||||||
|
CUSTOM_ARTIST_IMAGES,
|
||||||
|
PLAYLISTS,
|
||||||
|
QUEUE
|
||||||
|
}
|
6
app/src/main/res/drawable/rounded_drawable.xml
Normal file
6
app/src/main/res/drawable/rounded_drawable.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<solid android:color="?colorSurface"/>
|
||||||
|
<corners android:radius="28dp" />
|
||||||
|
</shape>
|
|
@ -1,8 +1,83 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
tools:context=".fragments.backup.RestoreActivity">
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/backupNameContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginVertical="8dp"
|
||||||
|
android:hint="@string/label_file_name">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/backupName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="none"/>
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/materialTextView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/choose_restore_title" />
|
||||||
|
|
||||||
|
<com.google.android.material.checkbox.MaterialCheckBox
|
||||||
|
android:id="@+id/check_settings"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:checked="true"
|
||||||
|
android:minHeight="48dp"
|
||||||
|
android:text="@string/action_settings" />
|
||||||
|
|
||||||
|
<com.google.android.material.checkbox.MaterialCheckBox
|
||||||
|
android:id="@+id/check_databases"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="48dp"
|
||||||
|
android:text="@string/databases_description" />
|
||||||
|
|
||||||
|
<com.google.android.material.checkbox.MaterialCheckBox
|
||||||
|
android:id="@+id/check_queue"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="48dp"
|
||||||
|
android:text="@string/now_playing_queue" />
|
||||||
|
|
||||||
|
<com.google.android.material.checkbox.MaterialCheckBox
|
||||||
|
android:id="@+id/check_user_images"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="48dp"
|
||||||
|
android:text="@string/user_images_description" />
|
||||||
|
|
||||||
|
<com.google.android.material.checkbox.MaterialCheckBox
|
||||||
|
android:id="@+id/check_artist_images"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="48dp"
|
||||||
|
android:text="@string/custom_artist_images" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="end">
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/cancel_button"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:text="@string/action_cancel" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/restore_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:text="@string/restore" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
|
@ -73,6 +73,7 @@
|
||||||
<string name="app_widget_big_name">Full Image</string>
|
<string name="app_widget_big_name">Full Image</string>
|
||||||
<string name="app_widget_card_name">Card</string>
|
<string name="app_widget_card_name">Card</string>
|
||||||
<string name="app_widget_classic_name">Classic</string>
|
<string name="app_widget_classic_name">Classic</string>
|
||||||
|
<string name="app_widget_md3_name">MD3</string>
|
||||||
<string name="app_widget_small_name">Small</string>
|
<string name="app_widget_small_name">Small</string>
|
||||||
<string name="app_widget_text_name">Minimal Text</string>
|
<string name="app_widget_text_name">Minimal Text</string>
|
||||||
<string name="artist">Artist</string>
|
<string name="artist">Artist</string>
|
||||||
|
@ -81,6 +82,7 @@
|
||||||
<string name="audio_focus_denied">Audio focus denied.</string>
|
<string name="audio_focus_denied">Audio focus denied.</string>
|
||||||
<string name="audio_settings_summary">Change the sound settings and adjust the equalizer controls</string>
|
<string name="audio_settings_summary">Change the sound settings and adjust the equalizer controls</string>
|
||||||
<string name="auto">Auto</string>
|
<string name="auto">Auto</string>
|
||||||
|
<string name="backup_restore_settings_summary">Backup and restore your settings, playlists</string>
|
||||||
<string name="backup_restore_title">
|
<string name="backup_restore_title">
|
||||||
<![CDATA[Backup & Restore]]>
|
<![CDATA[Backup & Restore]]>
|
||||||
</string>
|
</string>
|
||||||
|
@ -113,6 +115,7 @@
|
||||||
<string name="cascading">Cascading</string>
|
<string name="cascading">Cascading</string>
|
||||||
<string name="changelog">Changelog</string>
|
<string name="changelog">Changelog</string>
|
||||||
<string name="changelog_summary">Check out What\'s New</string>
|
<string name="changelog_summary">Check out What\'s New</string>
|
||||||
|
<string name="choose_restore_title">Choose what to restore</string>
|
||||||
<string name="circle">Circle</string>
|
<string name="circle">Circle</string>
|
||||||
<string name="circular">Circular</string>
|
<string name="circular">Circular</string>
|
||||||
<string name="classic">Classic</string>
|
<string name="classic">Classic</string>
|
||||||
|
@ -132,7 +135,9 @@
|
||||||
<string name="created_playlist_x">Created playlist %1$s.</string>
|
<string name="created_playlist_x">Created playlist %1$s.</string>
|
||||||
<string name="credit_title">Members and contributors </string>
|
<string name="credit_title">Members and contributors </string>
|
||||||
<string name="currently_listening_to_x_by_x">Currently listening to %1$s by %2$s.</string>
|
<string name="currently_listening_to_x_by_x">Currently listening to %1$s by %2$s.</string>
|
||||||
|
<string name="custom_artist_images">Custom Artist Images</string>
|
||||||
<string name="dark_theme_name">Kinda Dark</string>
|
<string name="dark_theme_name">Kinda Dark</string>
|
||||||
|
<string name="databases_description">Databases (Playlists, History, Most Played, etc.)</string>
|
||||||
<string name="delete_playlist_title">Delete playlist</string>
|
<string name="delete_playlist_title">Delete playlist</string>
|
||||||
<string name="delete_playlist_x">
|
<string name="delete_playlist_x">
|
||||||
<![CDATA[Delete the playlist <b>%1$s</b>?]]>
|
<![CDATA[Delete the playlist <b>%1$s</b>?]]>
|
||||||
|
@ -156,6 +161,7 @@
|
||||||
<string name="device_info">Device info</string>
|
<string name="device_info">Device info</string>
|
||||||
<string name="dialog_message_set_ringtone">Allow Retro Music to modify audio settings</string>
|
<string name="dialog_message_set_ringtone">Allow Retro Music to modify audio settings</string>
|
||||||
<string name="dialog_title_set_ringtone">Set ringtone</string>
|
<string name="dialog_title_set_ringtone">Set ringtone</string>
|
||||||
|
<string name="disc_hint">Disc Number</string>
|
||||||
<string name="do_you_want_to_clear_the_blacklist">Do you want to clear the blacklist?</string>
|
<string name="do_you_want_to_clear_the_blacklist">Do you want to clear the blacklist?</string>
|
||||||
<string name="do_you_want_to_remove_from_the_blacklist">
|
<string name="do_you_want_to_remove_from_the_blacklist">
|
||||||
<![CDATA[Do you want to remove <b>%1$s</b> from the blacklist?]]>
|
<![CDATA[Do you want to remove <b>%1$s</b> from the blacklist?]]>
|
||||||
|
@ -404,6 +410,7 @@
|
||||||
<string name="reset_action">Reset</string>
|
<string name="reset_action">Reset</string>
|
||||||
<string name="reset_artist_image">Reset artist image</string>
|
<string name="reset_artist_image">Reset artist image</string>
|
||||||
<string name="restore">Restore</string>
|
<string name="restore">Restore</string>
|
||||||
|
<string name="restore_message">Do you want to restore backup?</string>
|
||||||
<string name="restored_previous_purchase_please_restart">Restored previous purchase. Please restart the app to make use of all features.</string>
|
<string name="restored_previous_purchase_please_restart">Restored previous purchase. Please restart the app to make use of all features.</string>
|
||||||
<string name="restored_previous_purchases">Restored previous purchases.</string>
|
<string name="restored_previous_purchases">Restored previous purchases.</string>
|
||||||
<string name="restoring_purchase">Restoring purchase…</string>
|
<string name="restoring_purchase">Restoring purchase…</string>
|
||||||
|
@ -453,8 +460,8 @@
|
||||||
<string name="sort_order">Sort order</string>
|
<string name="sort_order">Sort order</string>
|
||||||
<string name="sort_order_a_z">Ascending</string>
|
<string name="sort_order_a_z">Ascending</string>
|
||||||
<string name="sort_order_album">Album</string>
|
<string name="sort_order_album">Album</string>
|
||||||
<string name="sort_order_artist">Artist</string>
|
|
||||||
<string name="sort_order_album_artist">@string/album_artist</string>
|
<string name="sort_order_album_artist">@string/album_artist</string>
|
||||||
|
<string name="sort_order_artist">Artist</string>
|
||||||
<string name="sort_order_composer">Composer</string>
|
<string name="sort_order_composer">Composer</string>
|
||||||
<string name="sort_order_date">Date added</string>
|
<string name="sort_order_date">Date added</string>
|
||||||
<string name="sort_order_date_modified">Date modified</string>
|
<string name="sort_order_date_modified">Date modified</string>
|
||||||
|
@ -480,6 +487,7 @@
|
||||||
<string name="tiny">Tiny</string>
|
<string name="tiny">Tiny</string>
|
||||||
<string name="tiny_card_style">Tiny card</string>
|
<string name="tiny_card_style">Tiny card</string>
|
||||||
<string name="title">Title</string>
|
<string name="title">Title</string>
|
||||||
|
<string name="title_new_backup">New Backup</string>
|
||||||
<string name="today">Today</string>
|
<string name="today">Today</string>
|
||||||
<string name="top_albums">Top albums</string>
|
<string name="top_albums">Top albums</string>
|
||||||
<string name="top_artists">Top artists</string>
|
<string name="top_artists">Top artists</string>
|
||||||
|
@ -495,6 +503,7 @@
|
||||||
<string name="up_next">Up next</string>
|
<string name="up_next">Up next</string>
|
||||||
<string name="update_image">Update image</string>
|
<string name="update_image">Update image</string>
|
||||||
<string name="updating">Updating…</string>
|
<string name="updating">Updating…</string>
|
||||||
|
<string name="user_images_description">User Images</string>
|
||||||
<string name="user_name">User Name</string>
|
<string name="user_name">User Name</string>
|
||||||
<string name="username">Username</string>
|
<string name="username">Username</string>
|
||||||
<string name="version">Version</string>
|
<string name="version">Version</string>
|
||||||
|
@ -515,9 +524,4 @@
|
||||||
<string name="you_have_to_select_at_least_one_category">You have to select at least one category.</string>
|
<string name="you_have_to_select_at_least_one_category">You have to select at least one category.</string>
|
||||||
<string name="you_will_be_forwarded_to_the_issue_tracker_website">You will be forwarded to the issue tracker website.</string>
|
<string name="you_will_be_forwarded_to_the_issue_tracker_website">You will be forwarded to the issue tracker website.</string>
|
||||||
<string name="your_account_data_is_only_used_for_authentication">Your account data is only used for authentication.</string>
|
<string name="your_account_data_is_only_used_for_authentication">Your account data is only used for authentication.</string>
|
||||||
<string name="restore_message">Do you want to restore backup?</string>
|
|
||||||
<string name="title_new_backup">New Backup</string>
|
|
||||||
<string name="backup_restore_settings_summary">Backup and restore your settings, playlists</string>
|
|
||||||
<string name="app_widget_md3_name">MD3</string>
|
|
||||||
<string name="disc_hint">Disc Number</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -236,4 +236,13 @@
|
||||||
<item name="colorAccent">@color/md_deep_purple_A400</item>
|
<item name="colorAccent">@color/md_deep_purple_A400</item>
|
||||||
<item name="colorPrimary">@color/md_deep_purple_500</item>
|
<item name="colorPrimary">@color/md_deep_purple_500</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="Theme.RetroMusic.Dialog" parent="Theme.Material3.DayNight.Dialog">
|
||||||
|
<item name="windowNoTitle">true</item>
|
||||||
|
<item name="android:windowIsFloating">true</item>
|
||||||
|
<item name="android:windowIsTranslucent">true</item>
|
||||||
|
<item name="android:windowBackground">@drawable/rounded_drawable</item>
|
||||||
|
<item name="android:windowFrame">@null</item>
|
||||||
|
<item name="background">@color/transparent</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue