From f90c1af8301a7d5814e117a5c93ae47e8216b4f6 Mon Sep 17 00:00:00 2001 From: Tim Schneeberger Date: Fri, 10 Mar 2023 15:13:55 +0100 Subject: [PATCH] feat: add min version check for presets for compatibility reasons --- .../rootlessjamesdsp/activity/MainActivity.kt | 12 ++++-- .../fragment/FileLibraryDialogFragment.kt | 12 ++++-- .../rootlessjamesdsp/model/Preset.kt | 39 +++++++++++++------ app/src/main/res/values/strings.xml | 1 + 4 files changed, 46 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/me/timschneeberger/rootlessjamesdsp/activity/MainActivity.kt b/app/src/main/java/me/timschneeberger/rootlessjamesdsp/activity/MainActivity.kt index 1a488a8c8..3f2ab6d7a 100644 --- a/app/src/main/java/me/timschneeberger/rootlessjamesdsp/activity/MainActivity.kt +++ b/app/src/main/java/me/timschneeberger/rootlessjamesdsp/activity/MainActivity.kt @@ -661,10 +661,16 @@ class MainActivity : BaseActivity() { CoroutineScope(Dispatchers.Default).launch { delay(250L) - if (name.endsWith(".tar")) - StorageUtils.openInputStreamSafe(this@MainActivity, uri)?.use { - Preset.load(this@MainActivity, it) + if (name.endsWith(".tar")) { + try { + StorageUtils.openInputStreamSafe(this@MainActivity, uri)?.use { + Preset.load(this@MainActivity, it) + } + } + catch (ex: Exception) { + showAlert(getString(R.string.filelibrary_corrupted_title), ex.localizedMessage ?: "") } + } else if (namespace != null && key != null && keyEnable != null) @Suppress("DEPRECATION") getSharedPreferences(namespace, MODE_MULTI_PROCESS) diff --git a/app/src/main/java/me/timschneeberger/rootlessjamesdsp/fragment/FileLibraryDialogFragment.kt b/app/src/main/java/me/timschneeberger/rootlessjamesdsp/fragment/FileLibraryDialogFragment.kt index b9ef8c258..484017b8d 100755 --- a/app/src/main/java/me/timschneeberger/rootlessjamesdsp/fragment/FileLibraryDialogFragment.kt +++ b/app/src/main/java/me/timschneeberger/rootlessjamesdsp/fragment/FileLibraryDialogFragment.kt @@ -453,11 +453,15 @@ class FileLibraryDialogFragment : ListPreferenceDialogFragmentCompat(), TargetFr val value = item.value if(fileLibPreference.isPreset()) { - val result = Preset(File(value.toString()).name).load() != null - if(result) + + try { + Preset(File(value.toString()).name).load() requireContext().toast(getString(R.string.filelibrary_preset_loaded, name)) - else - requireContext().toast(getString(R.string.filelibrary_preset_load_failed, name)) + } + catch (ex: Exception) { + requireContext().showAlert(getString(R.string.filelibrary_corrupted_title), + ex.localizedMessage ?: getString(R.string.filelibrary_preset_load_failed, name)) + } } clickedEntryValue = value diff --git a/app/src/main/java/me/timschneeberger/rootlessjamesdsp/model/Preset.kt b/app/src/main/java/me/timschneeberger/rootlessjamesdsp/model/Preset.kt index 0920fe8f2..d76a993fe 100644 --- a/app/src/main/java/me/timschneeberger/rootlessjamesdsp/model/Preset.kt +++ b/app/src/main/java/me/timschneeberger/rootlessjamesdsp/model/Preset.kt @@ -9,6 +9,7 @@ import me.timschneeberger.rootlessjamesdsp.liveprog.EelParser import me.timschneeberger.rootlessjamesdsp.utils.Constants import me.timschneeberger.rootlessjamesdsp.utils.Tar import me.timschneeberger.rootlessjamesdsp.utils.extensions.ContextExtensions.sendLocalBroadcast +import me.timschneeberger.rootlessjamesdsp.utils.extensions.ContextExtensions.showAlert import me.timschneeberger.rootlessjamesdsp.utils.extensions.ContextExtensions.toast import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -38,16 +39,15 @@ class Preset(val name: String): KoinComponent { return validate(FileInputStream(file())) } - fun load(): PresetMetadata? { + /** + * @exception Exception if preset cannot be loaded + */ + fun load(): PresetMetadata { val file = file() Timber.d("Loading preset from ${file.path}") return load( ctx, - try { FileInputStream(file) } - catch(ex: Exception) { - Timber.e(ex) - return null - } + FileInputStream(file) ) } @@ -67,10 +67,11 @@ class Preset(val name: String): KoinComponent { try { Tar.Composer(targetFile).use { c -> c.metadata = mutableMapOf( - META_VERSION to "2", + META_VERSION to PRESET_VERSION, META_APP_VERSION to BuildConfig.VERSION_NAME, META_APP_FLAVOR to BuildConfig.FLAVOR, - META_LIVEPROG_INCLUDED to false.toString() + META_LIVEPROG_INCLUDED to false.toString(), + META_MIN_VERSION_CODE to MIN_VERSION_CODE ) currentPath(ctx) @@ -107,32 +108,48 @@ class Preset(val name: String): KoinComponent { } companion object { + /* Update constants as needed */ + const val PRESET_VERSION = "3" + const val MIN_VERSION_CODE = "26" + const val FILE_LIVEPROG = "liveprog" const val META_VERSION = "version" const val META_APP_VERSION = "app_version" const val META_APP_FLAVOR = "app_flavor" const val META_LIVEPROG_INCLUDED = "liveprog_included" /* version 2+ */ + const val META_MIN_VERSION_CODE = "min_version_code" /* version 3+ */ private fun currentPath(ctx: Context) = File(ctx.applicationInfo.dataDir + "/shared_prefs") private fun isKnownEntry(n: String) = (n.startsWith("dsp_") && n.endsWith("xml")) || n == FILE_LIVEPROG fun validate(inputStream: InputStream) = Tar.Reader(inputStream, ::isKnownEntry).validate() - fun load(ctx: Context, stream: InputStream): PresetMetadata? { + /** + * @exception Exception if preset cannot be loaded + */ + fun load(ctx: Context, stream: InputStream): PresetMetadata { Timber.d("Loading preset from stream") val targetFolder = File(ctx.cacheDir, "preset") val metadata = Tar.Reader(stream, ::isKnownEntry).extract(targetFolder) - metadata ?: return null + metadata ?: throw Exception(ctx.getString(R.string.filelibrary_corrupted)) val version = metadata[META_VERSION]?.toIntOrNull() ?: 2 Timber.d("Loaded preset file version $version") + val minVersionCode = metadata[META_MIN_VERSION_CODE]?.toIntOrNull() ?: 0 + if(BuildConfig.VERSION_CODE < minVersionCode) { + Timber.w("Preset too new. Version code $minVersionCode or later required") + targetFolder.deleteRecursively() + throw Exception(ctx.getString(R.string.filelibrary_file_too_new)) + } + val files = targetFolder.listFiles() if(files == null || files.isEmpty()) { Timber.e("Preset archive did not contain any useful data") - return null + targetFolder.deleteRecursively() + throw Exception(ctx.getString(R.string.filelibrary_corrupted)) } files.forEach next@ { f -> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a64586845..bf321a909 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -396,6 +396,7 @@ The selected file has not have the correct file extension. Unsupported or corrupted file The selected file contains no useable data or is corrupted. + The selected file was created with a newer version of this application and cannnot be loaded. Please update this app to continue. Failed to access directory No file selected New file name