From 5f0f0321dbeeeecc921286c9b076d0186ea94ff3 Mon Sep 17 00:00:00 2001 From: Lucas Pinheiro Date: Wed, 20 Oct 2021 17:34:57 -0300 Subject: [PATCH 1/5] Update 1.2.0 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 629c5a8..15772a7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: on_audio_edit description: Flutter Plugin used to edit and read audios/songs infos/tags [Mp3, OggVorbis, Wav, etc...]. -version: 1.1.0 +version: 1.2.0 homepage: https://github.com/LucasPJS/on_audio_edit # pub.dev: https://pub.dev/packages/on_audio_query # ======== From 972e3e1796abc79c2e6aa92632715a81712f44b7 Mon Sep 17 00:00:00 2001 From: Lucas Pinheiro Date: Thu, 21 Oct 2021 14:07:34 -0300 Subject: [PATCH 2/5] Fixed `bug` with permission methods. --- .../on_audio_edit/OnAudioEditPlugin.kt | 547 ++++++++++-------- 1 file changed, 320 insertions(+), 227 deletions(-) diff --git a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/OnAudioEditPlugin.kt b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/OnAudioEditPlugin.kt index e35f254..f70adaa 100644 --- a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/OnAudioEditPlugin.kt +++ b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/OnAudioEditPlugin.kt @@ -17,6 +17,7 @@ import android.content.Intent import android.content.pm.PackageManager import android.net.Uri import android.os.Build +import android.util.Log import androidx.annotation.NonNull import androidx.annotation.RequiresApi import androidx.core.content.ContextCompat @@ -34,235 +35,327 @@ import io.flutter.plugin.common.MethodChannel.Result import io.flutter.plugin.common.PluginRegistry /** OnAudioEditPlugin */ -class OnAudioEditPlugin: FlutterPlugin, MethodCallHandler, ActivityAware, PluginRegistry.ActivityResultListener { - - // Dart <-> Kotlin communication - private val channelName = "com.lucasjosino.on_audio_edit" - private val channelError = "on_audio_error" - private lateinit var channel : MethodChannel - - // Main parameters - private var externalRequest: Boolean = false - private lateinit var activity: Activity - private lateinit var context: Context - private lateinit var result: Result - private lateinit var call: MethodCall - - // - private val onPermission = arrayOf( - Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE - ) - - // This is only important for initialization - override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - this.context = flutterPluginBinding.applicationContext - channel = MethodChannel(flutterPluginBinding.binaryMessenger, channelName) - channel.setMethodCallHandler(this) - } - - // Main method to communication between Dart <-> Kotlin - override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { - this.result = result ; this.call = call - // Check Basics Permissions - if (checkSimplePermissions()) { - when (call.method) { - // Permissions methods - "permissionsStatus" -> if (checkSimplePermissions()) result.success(true) else result.success(false) - - // Complex permission (Android 10) - "resetComplexPermission" -> resetComplexPermission() - "complexPermissionStatus" -> if (isSdCardGranted()) result.success(true) else result.success(false) - "requestComplexPermission" -> if (Build.VERSION.SDK_INT >= 29) { - openTree() - // When calling request Complex Permission from external(Dart), cancel the editing file. - // So, only will ask for permission, save it and return to Dart. - this.externalRequest = true - } else result.error(channelError, "[requestComplexPermission] it's only necessary on Android 10 or above", null) - - // Read methods - "readAudio" -> OnAudioRead().readAudio(result, call) - "readAllAudio" -> OnAudioRead().readAllAudio(result, call) - "readAudios" -> OnAudioRead().readAudios(result, call) - "readSingleAudioTag" -> OnAudioRead().readSingleAudioTag(result, call) - "readSpecificsAudioTags" -> OnAudioRead().readSpecificsAudioTags(result, call) - - // Write methods - "editAudio" -> if (Build.VERSION.SDK_INT < 29) OnAudioEdit(context).editAudio(result, call) - else editAudioController() - "editAudios" -> if (Build.VERSION.SDK_INT >= 29) - result.error(channelError, "Unfortunately this method isn't implemented on Android 10 and above, " + - "If you wanna help: https://github.com/LucasPJS/on_audio_edit", null) - else OnAudioEdit(context).editAudios(result, call) - - // Write artwork methods - "editArtwork" -> if (call.argument("openFilePicker")!!) getImageForArtwork(0) else { - if (Build.VERSION.SDK_INT >= 29) - OnArtworkEdit10(context, activity).editArtwork(result, call, null) - else OnArtworkEdit(context).editArtwork(result, call, null) +class OnAudioEditPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, + PluginRegistry.ActivityResultListener { + + // Dart <-> Kotlin communication + private val channelName = "com.lucasjosino.on_audio_edit" + private val channelError = "on_audio_error" + private lateinit var channel: MethodChannel + + // Main parameters + private var externalRequest: Boolean = false + private lateinit var activity: Activity + private lateinit var context: Context + private lateinit var result: Result + private lateinit var call: MethodCall + + // + private val onPermission = arrayOf( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE + ) + + // This is only important for initialization + override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + this.context = flutterPluginBinding.applicationContext + channel = MethodChannel(flutterPluginBinding.binaryMessenger, channelName) + channel.setMethodCallHandler(this) + } + + // Main method to communication between Dart <-> Kotlin + override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { + this.result = result; this.call = call + + // Warn the developer that this application don't have the [READ] and [WRITE] permissions. + if (checkSimplePermissions()) { + Log.w( + "on_audio_warning", + "This application don't have the [READ] and [WRITE] permissions." + ) + } + + // Check Basics Permissions + when (call.method) { + // Permissions methods + "permissionsStatus" -> result.success(checkSimplePermissions()) + + // Complex permission (Android 10) + "resetComplexPermission" -> resetComplexPermission() + "complexPermissionStatus" -> result.success(isSdCardGranted()) + "requestComplexPermission" -> { + if (Build.VERSION.SDK_INT >= 29) { + openTree() + // When calling request Complex Permission from external(Dart), cancel the editing file. + // So, only will ask for permission, save it and return to Dart. + this.externalRequest = true + } else { + result.success(false) + Log.w( + "on_audio_query_warning", + "[requestComplexPermission] it's only necessary on Android 10 or above" + ) + } + } + + // Read methods + "readAudio" -> OnAudioRead().readAudio(result, call) + "readAllAudio" -> OnAudioRead().readAllAudio(result, call) + "readAudios" -> OnAudioRead().readAudios(result, call) + "readSingleAudioTag" -> OnAudioRead().readSingleAudioTag(result, call) + "readSpecificsAudioTags" -> OnAudioRead().readSpecificsAudioTags(result, call) + + // Write methods + "editAudio" -> { + if (Build.VERSION.SDK_INT < 29) { + OnAudioEdit(context).editAudio(result, call) + } else { + editAudioController() + } + } + "editAudios" -> { + if (Build.VERSION.SDK_INT >= 29) { + result.error( + channelError, + "Unfortunately this method isn't implemented on Android 10 and above.", + null + ) + } else { + OnAudioEdit(context).editAudios(result, call) + } + } + + // Write artwork methods + "editArtwork" -> { + if (call.argument("openFilePicker")!!) { + getImageForArtwork(true) + } else { + if (Build.VERSION.SDK_INT >= 29) { + OnArtworkEdit10(context, activity).editArtwork(result, call, null) + } else { + OnArtworkEdit(context).editArtwork(result, call, null) + } + } + } + + // Delete methods + "deleteArtwork" -> { + if (Build.VERSION.SDK_INT >= 29) { + result.error( + channelError, + "Unfortunately this method isn't implemented on Android 10 and above.", + null + ) + } else { + OnAudioDelete(context).deleteArtwork(result, call) + } + } + "deleteArtworks" -> { + if (Build.VERSION.SDK_INT >= 29) { + result.error( + channelError, + "Unfortunately this method isn't implemented on Android 10 and above.", + null + ) + } else { + OnAudioDelete(context).deleteArtworks(result, call) + } + } + "deleteAudio" -> { + if (Build.VERSION.SDK_INT >= 29) { + result.error( + channelError, + "Unfortunately this method isn't implemented on Android 10 and above.", + null + ) + } else { + OnAudioDelete(context).audioDelete(result, call) + } + } + + // Image Picker + "getImagePath" -> getImageForArtwork(false) + + // Method don't exist + else -> result.notImplemented() + } + } + + override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) + } + + // This is only important for initialization - Start + override fun onAttachedToActivity(binding: ActivityPluginBinding) { + this.activity = binding.activity + binding.addActivityResultListener(this) + } + + override fun onDetachedFromActivityForConfigChanges() {} + + override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {} + + override fun onDetachedFromActivity() {} + // End + + // Check basics permissions + // Return true if both read and write permissions are granted or false if not. + private fun checkSimplePermissions(): Boolean = onPermission.all { + return ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED + } + + // Artwork picker + + // + private val onImagePickerInternalCode = 88561 + private val onImagePickerExternalCode = 88562 + + // Open the image folder to user select. + private fun getImageForArtwork(isInternal: Boolean) { + val code = if (isInternal) onImagePickerInternalCode else onImagePickerExternalCode + val intent = Intent(Intent.ACTION_PICK) + intent.type = "image/*" + activity.startActivityForResult(Intent.createChooser(intent, "Select Picture"), code) + } + + // Check complex permission for Android 29/Q/10 + + // Main parameters + private val onAudioEditCode = 8856 + private val onSharedPrefKeyRequestCode = "on_audio_edit_requestPermission" + private val onSharedPrefKeyUriCode = "on_audio_edit_uri" + private lateinit var uri: Uri + + // Check if plugin already has permission. + private fun isSdCardGranted(): Boolean = activity.getSharedPreferences( + "on_audio_edit", + Context.MODE_PRIVATE + ).getBoolean(onSharedPrefKeyRequestCode, false) + + private fun editAudioController() { + if (Build.VERSION.SDK_INT > 21) { + if (isSdCardGranted()) OnAudioEdit10(context, activity).editAudio10( + result, + call + ) else openTree() } + } + + // Open the tree(folder screen) to user select. + // [SHOW_ADVANCED] will show to user more specific description about what we need. + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + private fun openTree() { + val advancedIntent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) + advancedIntent.putExtra("android.content.extra.SHOW_ADVANCED", true) + activity.startActivityForResult(advancedIntent, onAudioEditCode) + } - // Delete methods - "deleteArtwork" -> if (Build.VERSION.SDK_INT >= 29) - result.error(channelError, "Unfortunately this method isn't implemented on Android 10 and above, " + - "If you wanna help: https://github.com/LucasPJS/on_audio_edit", null) - else OnAudioDelete(context).deleteArtwork(result, call) - "deleteArtworks" -> if (Build.VERSION.SDK_INT >= 29) - result.error(channelError, "Unfortunately this method isn't implemented on Android 10 and above, " + - "If you wanna help: https://github.com/LucasPJS/on_audio_edit", null) - else OnAudioDelete(context).deleteArtworks(result, call) - "deleteAudio" -> if (Build.VERSION.SDK_INT >= 29) - result.error(channelError, "Unfortunately this method isn't implemented on Android 10 and above, " + - "If you wanna help: https://github.com/LucasPJS/on_audio_edit", null) - else OnAudioDelete(context).audioDelete(result, call) - - // Image Picker - "getImagePath" -> getImageForArtwork(1) - - // Method don't exist - else -> result.notImplemented() - } - } else result.error(channelError, "[on_audio_edit] needs [READ] and [WRITE] permissions to work.", null) - } - - override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { - channel.setMethodCallHandler(null) - } - - // This is only important for initialization - Start - override fun onAttachedToActivity(binding: ActivityPluginBinding) { - this.activity = binding.activity - binding.addActivityResultListener(this) - } - - override fun onDetachedFromActivityForConfigChanges() {} - - override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {} - - override fun onDetachedFromActivity() {} - // End - - // Check basics permissions - // Return true if both read and write permissions are granted or false if are't. - private fun checkSimplePermissions(): Boolean = onPermission.all { - return ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED - } - - // Artwork picker - - // - private val onImagePickerInternalCode = 88561 - private val onImagePickerExternalCode = 88562 - - // Open the image folder to user select. - private fun getImageForArtwork(IorE: Int) { - val code = if (IorE == 0) onImagePickerInternalCode else onImagePickerExternalCode - val intent = Intent(Intent.ACTION_PICK) - intent.type = "image/*" - activity.startActivityForResult(Intent.createChooser(intent, "Select Picture"), code) - } - - // Check complex permission for Android 29/Q/10 - - // Main parameters - private val onAudioEditCode = 8856 - private val onSharedPrefKeyRequestCode = "on_audio_edit_requestPermission" - private val onSharedPrefKeyUriCode = "on_audio_edit_uri" - private lateinit var uri: Uri - - // Check if plugin already has permission. - private fun isSdCardGranted() : Boolean = activity.getSharedPreferences("on_audio_edit", - Context.MODE_PRIVATE).getBoolean(onSharedPrefKeyRequestCode, false) - - private fun editAudioController() { - if (Build.VERSION.SDK_INT > 21) { - if (isSdCardGranted()) OnAudioEdit10(context, activity).editAudio10(result, call) else openTree() + // Get persistent permission to avoid multiples request and save it using shared pref. + // Probably will add options for more folders. Cuz, user can only select one folder with this method. + // > [can only select one folder] if folder has subfolder document file still providing permission. + @RequiresApi(Build.VERSION_CODES.KITKAT) + private fun onSaveTree() { + val intentRead = Intent.FLAG_GRANT_READ_URI_PERMISSION + val intentWrite = Intent.FLAG_GRANT_WRITE_URI_PERMISSION + context.contentResolver.takePersistableUriPermission(uri, intentRead or intentWrite) + // Save + val sharedPref = activity.getSharedPreferences("on_audio_edit", Context.MODE_PRIVATE) + sharedPref.edit().putBoolean(onSharedPrefKeyRequestCode, true).apply() + sharedPref.edit().putString(onSharedPrefKeyUriCode, uri.toString()).apply() } - } - - // Open the tree(folder screen) to user select. - // [SHOW_ADVANCED] will show to user more specific description about what we need. - @RequiresApi(Build.VERSION_CODES.LOLLIPOP) - private fun openTree() { - val advancedIntent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - advancedIntent.putExtra("android.content.extra.SHOW_ADVANCED", true) - activity.startActivityForResult(advancedIntent, onAudioEditCode) - } - - // Get persistent permission to avoid multiples request and save it using shared pref. - // Probably will add options for more folders. Cuz, user can only select one folder with this method. - // > [can only select one folder] if folder has subfolder document file still providing permission. - @RequiresApi(Build.VERSION_CODES.KITKAT) - private fun onSaveTree() { - val intentRead = Intent.FLAG_GRANT_READ_URI_PERMISSION - val intentWrite = Intent.FLAG_GRANT_WRITE_URI_PERMISSION - context.contentResolver.takePersistableUriPermission(uri, intentRead or intentWrite) - //Save for next time - val sharedPref = activity.getSharedPreferences("on_audio_edit", Context.MODE_PRIVATE) - sharedPref.edit().putBoolean(onSharedPrefKeyRequestCode, true).apply() - sharedPref.edit().putString(onSharedPrefKeyUriCode, uri.toString()).apply() - } - - // Reset shared prefs with uri. - private fun resetComplexPermission() { - val sharedPref = activity.getSharedPreferences("on_audio_edit", Context.MODE_PRIVATE) - sharedPref.edit().remove(onSharedPrefKeyRequestCode).apply() - sharedPref.edit().remove(onSharedPrefKeyUriCode).apply() - result.success(true) - } - - // OnEditPermissionController - // It's ugly but functional: - // - // Important: - // * This is only necessary on Android >= 10/Q, cuz, google has changed storage permission. - // - // #onAudioEditCode - // * Check if [requestCode] or [resultData] is null, if true, throw a error and back to dart, else - // call [onSaveTree] that will save uri and avoid multiples folders select from the user. After all this, - // check if the folder request is from internal or external, if it's from external, only save thee and back to dart, - // else, go to edit audio. - // - // #onImagePickerInternalCode - // * Check if [requestCode] or [resultData] is null, if true, throw a error and back to dart, else - // call edit artwork. - // - // #onImagePickerExternalCode - // * Check if [requestCode] or [resultData] is null, if true, throw a error and back to dart, else - // go back to Dart with image uri. - // - // If the requestCode is null, throw a error. Maaaybe, my fault :/ - @RequiresApi(Build.VERSION_CODES.KITKAT) - override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?): Boolean { - // Edit normal fields - when (requestCode) { - onAudioEditCode -> { - if (resultCode == RESULT_OK && resultData != null) { - resultData.data?.also { uri -> this.uri = uri } - onSaveTree() - if (!externalRequest) OnAudioEdit10(context, activity).editAudio10(result, call) - return true - } else result.error(channelError, "[resultCode] or [resultData] returned null.", null) ; return false - } - // Edit artwork field - onImagePickerInternalCode -> { - if (resultCode == RESULT_OK && resultData != null) { - resultData.data?.also { - if (Build.VERSION.SDK_INT >= 29) - OnArtworkEdit10(context, activity).editArtwork(result, call, resultData.data!!) - else OnArtworkEdit(context).editArtwork(result, call, resultData.data!!) - return true - } - } else result.error(channelError, "[OIPIC] - [resultCode] or [resultData] returned null.", null) ; return false - } - onImagePickerExternalCode -> { - if (resultCode == RESULT_OK && resultData != null) { - result.success(resultData.data.toString()) - return true - } else result.error(channelError, "[OIPEC] - [resultCode] or [resultData] returned null.", null) ; return false - } - else -> result.error(channelError, "RequestCode: [$requestCode] don't exist, probably my fault, open a issue in GitHub", null) + + // Reset shared prefs with uri. + private fun resetComplexPermission() { + val sharedPref = activity.getSharedPreferences("on_audio_edit", Context.MODE_PRIVATE) + sharedPref.edit().remove(onSharedPrefKeyRequestCode).apply() + sharedPref.edit().remove(onSharedPrefKeyUriCode).apply() + result.success(true) + } + + // Important: + // * This is only necessary on Android >= 10/Q. + // + // #onAudioEditCode + // * Check if [requestCode] or [resultData] is null, if true, throw a error and back to dart, else + // call [onSaveTree] that will save uri and avoid multiples folders select from the user. After all this, + // check if the folder request is from internal or external, if it's from external, only save thee and back to dart, + // else, go to edit audio. + // + // #onImagePickerInternalCode + // * Check if [requestCode] or [resultData] is null, if true, throw a error and back to dart, else + // call edit artwork. + // + // #onImagePickerExternalCode + // * Check if [requestCode] or [resultData] is null, if true, throw a error and back to dart, else + // go back to Dart with image uri. + @RequiresApi(Build.VERSION_CODES.KITKAT) + override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?): Boolean { + // When [result] is not initialized the permission request did not originate from the + // [on_audio_edit] plugin, so return [false] to indicate the [on_audio_edit] plugin is not + // handling the request result and Android should continue executing other registered handlers. + if (!this::result.isInitialized) return false + + // When the incoming request code doesn't match the request codes defined by the [on_audio_edit] + // plugin return [false] to indicate the [on_audio_edit] plugin is not handling the request + // result and Android should continue executing other registered handlers. + if (requestCode != onAudioEditCode + && requestCode != onImagePickerInternalCode + && requestCode != onImagePickerExternalCode + ) return false + + // Edit normal fields + when (requestCode) { + onAudioEditCode -> { + if (resultCode == RESULT_OK && resultData != null) { + resultData.data?.also { uri -> this.uri = uri } + onSaveTree() + if (!externalRequest) { + OnAudioEdit10(context, activity).editAudio10(result, call) + } else { + result.success(true) + } + } else { + result.error(channelError, "[resultCode] or [resultData] returned null.", null) + } + } + // Edit artwork field + onImagePickerInternalCode -> { + if (resultCode == RESULT_OK && resultData != null) { + resultData.data?.also { + if (Build.VERSION.SDK_INT >= 29) + OnArtworkEdit10(context, activity).editArtwork( + result, + call, + resultData.data!! + ) + else { + OnArtworkEdit(context).editArtwork(result, call, resultData.data!!) + } + } + } else { + result.error( + channelError, + "[OIPIC] - [resultCode] or [resultData] returned null.", + null + ) + } + } + onImagePickerExternalCode -> { + if (resultCode == RESULT_OK && resultData != null) { + result.success(resultData.data.toString()) + } else { + result.error( + channelError, + "[OIPEC] - [resultCode] or [resultData] returned null.", + null + ) + } + } + else -> { + result.error(channelError, "RequestCode: [$requestCode] don't exist.", null) + return false + } + } + return true } - return false - } } From 7db8256e4d93c6b08df2734916065380c07b50eb Mon Sep 17 00:00:00 2001 From: Lucas Pinheiro Date: Thu, 21 Oct 2021 17:59:20 -0300 Subject: [PATCH 3/5] Part of update `1.2.0` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix no `ALBUM` tag in `TagType`. (✔️) Add feature to search inside all selected folder when using Android 10 or above. (✔️) Change `readAudio`, `readAllAudio` and `readSpecificsAudioTags` return type from `Map` to `AudioModel`. (✔️) Discontinue `readAllAudio`. (✔️) --- DEPRECATED.md | 5 ++ .../on_audio_edit/OnAudioEditPlugin.kt | 3 +- .../methods/edits/OnAudioEdit10.kt | 40 ++++++++--- .../on_audio_edit/methods/read/OnAudioRead.kt | 21 +----- .../on_audio_edit/types/TagType.kt | 67 ++++++++++--------- example/lib/main.dart | 8 ++- lib/details/on_audio_edit_controller.dart | 65 +++++++----------- lib/details/types/tag_type.dart | 3 + 8 files changed, 104 insertions(+), 108 deletions(-) diff --git a/DEPRECATED.md b/DEPRECATED.md index b7ad28c..9adc836 100644 --- a/DEPRECATED.md +++ b/DEPRECATED.md @@ -1,3 +1,8 @@ +## [1.2.0] - [10.20.2021] -> [X.X.X] - [XX.XX.XXXX] +### Deprecated +- `[readAllAudio]`. + - Use `[readAudio]` instead. + ## [1.1.0] - [10.20.2021] -> [X.X.X] - [XX.XX.XXXX] ### Deprecated - `[AudiosTagModel]`. diff --git a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/OnAudioEditPlugin.kt b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/OnAudioEditPlugin.kt index f70adaa..dee0a1a 100644 --- a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/OnAudioEditPlugin.kt +++ b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/OnAudioEditPlugin.kt @@ -68,7 +68,7 @@ class OnAudioEditPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, this.result = result; this.call = call // Warn the developer that this application don't have the [READ] and [WRITE] permissions. - if (checkSimplePermissions()) { + if (!checkSimplePermissions()) { Log.w( "on_audio_warning", "This application don't have the [READ] and [WRITE] permissions." @@ -100,7 +100,6 @@ class OnAudioEditPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, // Read methods "readAudio" -> OnAudioRead().readAudio(result, call) - "readAllAudio" -> OnAudioRead().readAllAudio(result, call) "readAudios" -> OnAudioRead().readAudios(result, call) "readSingleAudioTag" -> OnAudioRead().readSingleAudioTag(result, call) "readSpecificsAudioTags" -> OnAudioRead().readSpecificsAudioTags(result, call) diff --git a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/methods/edits/OnAudioEdit10.kt b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/methods/edits/OnAudioEdit10.kt index b91b95b..dea3948 100644 --- a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/methods/edits/OnAudioEdit10.kt +++ b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/methods/edits/OnAudioEdit10.kt @@ -28,14 +28,17 @@ class OnAudioEdit10(private val context: Context, private val activity: Activity // Main parameters private val channelError = "on_audio_error" private val onSharedPrefKeyUriCode = "on_audio_edit_uri" + private var searchInsideFolders: Boolean = false private lateinit var data: String private lateinit var getTagsAndInfo: MutableMap // Edit File to Android >= 29/Q/10 // Check if plugin already has uri. - private fun getUri() : String? = activity.getSharedPreferences("on_audio_edit", - Context.MODE_PRIVATE).getString(onSharedPrefKeyUriCode, "") + private fun getUri(): String? = activity.getSharedPreferences( + "on_audio_edit", + Context.MODE_PRIVATE + ).getString(onSharedPrefKeyUriCode, "") // Android 10 has a bug on Storage system, to edit some audio we need ask user permission on specific folder. // This extra permission has already been accepted (OnAudioEditPlugin.kt -> openTree()) at this moment and uri it's already saved. @@ -46,12 +49,14 @@ class OnAudioEdit10(private val context: Context, private val activity: Activity // Get all information from Dart. this.data = call.argument("data")!! + this.searchInsideFolders = call.argument("searchInsideFolders")!! val mapTagsAndInfo: MutableMap = call.argument("tags")!! // Converting int to FieldKey this.getTagsAndInfo = EnumMap(FieldKey::class.java) mapTagsAndInfo.forEach { keyOrValue -> - if (checkTag(keyOrValue.key) != null) getTagsAndInfo[checkTag(keyOrValue.key)!!] = keyOrValue.value + if (checkTag(keyOrValue.key) != null) getTagsAndInfo[checkTag(keyOrValue.key)!!] = + keyOrValue.value } // Do everything in background to avoid bad performance. @@ -65,7 +70,7 @@ class OnAudioEdit10(private val context: Context, private val activity: Activity } @Suppress("BlockingMethodInNonBlockingContext") - private suspend fun doEverythingInBackground() : Boolean = withContext(Dispatchers.IO) { + private suspend fun doEverythingInBackground(): Boolean = withContext(Dispatchers.IO) { val internalData = File(data) // Get and check if uri is null. @@ -73,11 +78,10 @@ class OnAudioEdit10(private val context: Context, private val activity: Activity // Use DocumentFile to navigate and find specific data inside specific folder. // We need this, cuz google blocked some access in Android >= 10/Q - var pUri: Uri = Uri.parse("") // This can be null but, we use inside try / catch - val dFile = DocumentFile.fromTreeUri(context, uriFolder) - // [findFile] will give a slow performance, so, we use Kotlin Coroutines and "doEverythingInBackground" - val fileList = dFile!!.findFile(internalData.name) - if (fileList != null) pUri = fileList.uri + val pUri: Uri + val dFile = DocumentFile.fromTreeUri(context, uriFolder) ?: return@withContext false + // [getFile] will give a slow performance, so, we use Kotlin Coroutines and "doEverythingInBackground" + pUri = getFile(dFile, internalData)?.uri ?: return@withContext false // Temp file just to write(rewrite) file path. Produce the same result as "scan" val temp = File.createTempFile("tmp-media", '.' + Utils.getExtension(internalData)) @@ -97,7 +101,9 @@ class OnAudioEdit10(private val context: Context, private val activity: Activity try { AudioFileIO.write(audioFile) - } catch (e: Exception) { Log.i(channelError, e.toString()) } + } catch (e: Exception) { + Log.i(channelError, e.toString()) + } // Start setup to write in folder // Until this moment we only write inside audio file, but we need tell android that this file has some change. @@ -132,6 +138,20 @@ class OnAudioEdit10(private val context: Context, private val activity: Activity return@withContext false } + private fun getFile(directory: DocumentFile, specificFile: File): DocumentFile? { + val files = directory.listFiles() + for (file in files) { + val data: DocumentFile? = if (file.isDirectory) { + // If [searchInsideFolders] is true, we keep searching, if not, no file was found. + if (searchInsideFolders) getFile(file, specificFile) else return null + } else { + if (file.name == specificFile.name) file else null + } + if (data != null) return data + } + return null + } + // TODO Edit Multiples Audios on Android >= 29/Q/10 // fun editAudios10(result: MethodChannel.Result, call: MethodCall) { diff --git a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/methods/read/OnAudioRead.kt b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/methods/read/OnAudioRead.kt index 348ccc8..cf74913 100644 --- a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/methods/read/OnAudioRead.kt +++ b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/methods/read/OnAudioRead.kt @@ -1,6 +1,5 @@ package com.lucasjosino.on_audio_edit.methods.read -import android.util.Log import com.lucasjosino.on_audio_edit.types.checkTag import com.lucasjosino.on_audio_edit.utils.checkAndGetExtraInfo import com.lucasjosino.on_audio_edit.utils.getAllProjection @@ -26,8 +25,7 @@ class OnAudioRead { // Getting all tags val tagsData: MutableMap = HashMap() - for (tag in getProjection()) tagsData[tag.name] = audioTag.getValue(tag, 0).orEmpty() - Log.i("CheckCoverArt", tagsData["COVER_ART"].toString()) + for (tag in getAllProjection()) tagsData[tag.name] = audioTag.getValue(tag, 0).orEmpty() // Extra information tagsData.putAll(checkAndGetExtraInfo(audioFile)) @@ -36,23 +34,6 @@ class OnAudioRead { result.success(tagsData) } - fun readAllAudio(result: MethodChannel.Result, call: MethodCall) { - // Get all information from Dart. - val data = call.argument("data")!! - - // Setup - val audioData = File(data) - val audioFile = AudioFileIO.read(audioData) - val audioTag = audioFile.tag - - // Getting all tags - val tagsData: MutableMap = HashMap() - for (tag in getAllProjection()) tagsData[tag.name] = audioTag.getValue(tag, 0).orEmpty() - - // Sending to Dart - result.success(tagsData) - } - // fun readAudios(result: MethodChannel.Result, call: MethodCall) { // Get all information from Dart. diff --git a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/types/TagType.kt b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/types/TagType.kt index d0c34f7..3e00dfe 100644 --- a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/types/TagType.kt +++ b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/types/TagType.kt @@ -4,39 +4,40 @@ import org.jaudiotagger.tag.FieldKey fun checkTag(tag: Int) : FieldKey? { return when (tag) { - 0 -> FieldKey.ALBUM_ARTIST - 1 -> FieldKey.ARTIST - 2 -> FieldKey.ARTISTS - 3 -> FieldKey.BPM -// FieldKey.BITRATE, 4 -// FieldKey.CHANNELS, 5 - 6 -> FieldKey.COMPOSER - 7 -> FieldKey.COUNTRY - 8 -> FieldKey.COVER_ART -// FieldKey.FIRST_ARTWORK, 9 -// FieldKey.FORMAT, 10 - 11 -> FieldKey.GENRE - 12 -> FieldKey.ISRC - 13 -> FieldKey.KEY - 14 -> FieldKey.LANGUAGE -// FieldKey.LENGTH, 15 - 16 -> FieldKey.LYRICS - 17 -> FieldKey.ORIGINAL_ALBUM - 18 -> FieldKey.ORIGINAL_ARTIST - 19 -> FieldKey.ORIGINAL_LYRICIST - 20 -> FieldKey.ORIGINAL_YEAR - 21 -> FieldKey.PRODUCER - 22 -> FieldKey.QUALITY - 23-> FieldKey.RATING - 24 -> FieldKey.RECORD_LABEL -// FieldKey.SAMPLE_RATE, 25 - 26 -> FieldKey.SUBTITLE - 27 -> FieldKey.TAGS - 28 -> FieldKey.TEMPO - 29 -> FieldKey.TITLE - 30 -> FieldKey.TRACK -// FieldKey.TYPE, 31 - 32 -> FieldKey.YEAR + 0 -> FieldKey.ALBUM + 1 -> FieldKey.ALBUM_ARTIST + 2 -> FieldKey.ARTIST + 3 -> FieldKey.ARTISTS + 4 -> FieldKey.BPM +// FieldKey.BITRATE, 5 +// FieldKey.CHANNELS, 6 + 7 -> FieldKey.COMPOSER + 8 -> FieldKey.COUNTRY + 9 -> FieldKey.COVER_ART +// FieldKey.FIRST_ARTWORK, 10 +// FieldKey.FORMAT, 11 + 12 -> FieldKey.GENRE + 13 -> FieldKey.ISRC + 14 -> FieldKey.KEY + 15 -> FieldKey.LANGUAGE +// FieldKey.LENGTH, 16 + 17 -> FieldKey.LYRICS + 18 -> FieldKey.ORIGINAL_ALBUM + 19 -> FieldKey.ORIGINAL_ARTIST + 20 -> FieldKey.ORIGINAL_LYRICIST + 21 -> FieldKey.ORIGINAL_YEAR + 22 -> FieldKey.PRODUCER + 23 -> FieldKey.QUALITY + 24-> FieldKey.RATING + 25 -> FieldKey.RECORD_LABEL +// FieldKey.SAMPLE_RATE, 26 + 27 -> FieldKey.SUBTITLE + 28 -> FieldKey.TAGS + 29 -> FieldKey.TEMPO + 30 -> FieldKey.TITLE + 31 -> FieldKey.TRACK +// FieldKey.TYPE, 32 + 33 -> FieldKey.YEAR else -> null } } \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index 1123039..77803b0 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -12,6 +12,8 @@ Copyright: © 2021, Lucas Josino. All rights reserved. ============= */ +import 'dart:developer'; + import 'package:flutter/material.dart'; import 'package:on_audio_edit/on_audio_edit.dart'; import 'package:on_audio_query/on_audio_query.dart'; @@ -102,11 +104,11 @@ class _MyAppState extends State with SingleTickerProviderStateMixin { onTap: () => optionsDialog(context, index), // [onLongPress] will read all information about selected items: onLongPress: () async { - var result = await _audioEdit.readAudios( - [songList[index].data], + var result = await _audioEdit.readAllAudio( + songList[index].data, ); // Print the result. - debugPrint(result[0].toString()); + log(result.toString()); }, title: Text(songList[index].title), subtitle: Text( diff --git a/lib/details/on_audio_edit_controller.dart b/lib/details/on_audio_edit_controller.dart index 0e48356..bf24ed9 100644 --- a/lib/details/on_audio_edit_controller.dart +++ b/lib/details/on_audio_edit_controller.dart @@ -25,55 +25,40 @@ class OnAudioEdit { /// Parameters: /// /// * [data] is used for find specific audio data. + /// * [searchInsideFolders] is used for find specific audio data even inside + /// the folders. **(Only required when using Android 10 or above)** /// /// Usage: /// /// ```dart - /// Map song = await OnAudioEdit().readAudio(data); - /// var songTitle = song["TITLE"]; - /// var songArtist = song["ARTIST"]; + /// AudioModel song = await OnAudioEdit().readAudio(data); + /// String songTitle = song.title; + /// String songArtist = song.artist; /// ``` /// /// Important: /// - /// * Calling any method without [READ] and [WRITE] permission will throw a error. + /// * Calling any method without [READ] and [WRITE] permission will send a warning. + /// /// Use [permissionsStatus] to see permissions status. - /// * Most audios have no information other than the [title] and [artist]. - /// * This method return a [Map]. - Future> readAudio(String data) async { - final Map resultReadAudio = - await _channel.invokeMethod("readAudio", { + Future readAudio( + String data, { + bool searchInsideFolders = false, + }) async { + final Map resultReadAudio = await _channel.invokeMethod("readAudio", { "data": data, + "searchInsideFolders": searchInsideFolders, }); - return resultReadAudio; + return AudioModel(resultReadAudio); } - /// Used to return all possible info of a unique song. - /// - /// Parameters: - /// - /// * [data] is used for find specific audio data. - /// - /// Usage: - /// - /// ```dart - /// Map song = await OnAudioEdit().readAudio(data); - /// var songTitle = song["TITLE"]; - /// var songArtist = song["ARTIST"]; - /// ``` - /// - /// Important: - /// - /// * Calling any method without [READ] and [WRITE] permission will throw a error. - /// Use [permissionsStatus] to see permissions status. - /// * Most audios have no information other than the [title] and [artist]. - /// * This method return a [Map]. - Future> readAllAudio(String data) async { - final Map resultReadAudio = - await _channel.invokeMethod("readAllAudio", { - "data": data, - }); - return resultReadAudio; + /// Deprecated after [1.2.0] + @Deprecated('Use [readAudio] instead') + Future readAllAudio( + String data, { + bool searchInsideFolders = false, + }) async { + return readAudio(data, searchInsideFolders: searchInsideFolders); } /// Used to return multiples songs info. @@ -94,8 +79,8 @@ class OnAudioEdit { /// Important: /// /// * Calling any method without [READ] and [WRITE] permission will throw a error. + /// /// Use [permissionsStatus] to see permissions status. - /// * Most audios have no information other than the [title] and [artist]. Future> readAudios(List data) async { final List resultReadAudio = await _channel.invokeMethod("readAudios", { @@ -124,8 +109,8 @@ class OnAudioEdit { /// Important: /// /// * Calling any method without [READ] and [WRITE] permission will throw a error. + /// /// Use [permissionsStatus] to see permissions status. - /// * Most audios have no information other than the [title] and [artist]. Future readSingleAudioTag(String data, TagType tag) async { final String resultSingleAudioTag = await _channel.invokeMethod("readSingleAudioTag", { @@ -161,7 +146,7 @@ class OnAudioEdit { /// Use [permissionsStatus] to see permissions status. /// * Most audios have no information other than the [title] and [artist]. /// * This method return a [Map]. - Future> readSpecificsAudioTags( + Future readSpecificsAudioTags( String data, List tags) async { List tagsIndex = []; for (var it in tags) { @@ -172,7 +157,7 @@ class OnAudioEdit { "data": data, "tags": tagsIndex, }); - return readSpecificsAudioTags; + return AudioModel(readSpecificsAudioTags); } /// Used to edit song info. diff --git a/lib/details/types/tag_type.dart b/lib/details/types/tag_type.dart index ce8659e..2269876 100644 --- a/lib/details/types/tag_type.dart +++ b/lib/details/types/tag_type.dart @@ -4,6 +4,9 @@ part of on_audio_edit; /// All the songs tags type. enum TagType { + /// + ALBUM, + /// ALBUM_ARTIST, From 532c4093a66784cbc51ce05d890496f256dba04d Mon Sep 17 00:00:00 2001 From: Lucas Pinheiro Date: Fri, 22 Oct 2021 14:46:50 -0300 Subject: [PATCH 4/5] Part of update `1.2.0` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the `getImagePath` return from `String` to `Bytes(Uint8List)`. (✔️) Discontinue `readAllAudio` and `getImagePath`. (✔️) Create `ImageModel`. (✔️) Add `ALL` possible tag type. (✔️) Add `ALL` possible getter to `AudioModel`. (✔️) --- .../on_audio_edit/OnAudioEditPlugin.kt | 39 ++- .../extensions/OnHelperExtension.kt | 32 +++ .../on_audio_edit/methods/read/OnAudioRead.kt | 43 ++- .../on_audio_edit/types/TagType.kt | 118 +++++--- .../on_audio_edit/utils/OnArtworkFormat.kt | 15 +- .../on_audio_edit/utils/OnExtraInfo.kt | 28 +- .../on_audio_edit/utils/OnTagsProjections.kt | 265 ++++++------------ example/lib/main.dart | 6 +- lib/details/models/audio_model.dart | 249 +++++++++++++--- lib/details/models/image_model.dart | 25 ++ lib/details/on_audio_edit_controller.dart | 128 ++++++--- lib/details/types/artwork_type.dart | 3 - lib/details/types/tag_type.dart | 248 +++++++++++----- lib/on_audio_edit.dart | 3 + 14 files changed, 784 insertions(+), 418 deletions(-) create mode 100644 android/src/main/kotlin/com/lucasjosino/on_audio_edit/extensions/OnHelperExtension.kt create mode 100644 lib/details/models/image_model.dart diff --git a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/OnAudioEditPlugin.kt b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/OnAudioEditPlugin.kt index dee0a1a..912847b 100644 --- a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/OnAudioEditPlugin.kt +++ b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/OnAudioEditPlugin.kt @@ -1,10 +1,15 @@ /* +============= Author: Lucas Josino Github: https://github.com/LucasPJS -Plugin: on_audio_edit +Website: https://lucasjosino.com/ +============= +Plugin/Id: on_audio_edit#3 Homepage: https://github.com/LucasPJS/on_audio_edit -Copyright: © 2021, Lucas Josino. All rights reserved. +Pub: https://pub.dev/packages/on_audio_edit License: https://github.com/LucasPJS/on_audio_edit/blob/main/LICENSE +Copyright: © 2021, Lucas Josino. All rights reserved. +============= */ package com.lucasjosino.on_audio_edit @@ -34,6 +39,12 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result import io.flutter.plugin.common.PluginRegistry +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import com.lucasjosino.on_audio_edit.utils.checkArtworkFormatBitmap +import java.io.ByteArrayOutputStream +import java.io.FileOutputStream + /** OnAudioEditPlugin */ class OnAudioEditPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, PluginRegistry.ActivityResultListener { @@ -341,7 +352,12 @@ class OnAudioEditPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, } onImagePickerExternalCode -> { if (resultCode == RESULT_OK && resultData != null) { - result.success(resultData.data.toString()) + val imageData: MutableMap = HashMap() + val file = FileOutputStream(resultData.dataString) + val bitmap = BitmapFactory.decodeFileDescriptor(file.fd) + imageData["imageBytes"] = convertBitmap(bitmap) + imageData["imageData"] = resultData.dataString + result.success(imageData) } else { result.error( channelError, @@ -357,4 +373,21 @@ class OnAudioEditPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, } return true } + + // + private fun convertBitmap(bitmap: Bitmap): ByteArray? { + val convertedBytes: ByteArray? + val byteArrayBase = ByteArrayOutputStream() + val format = checkArtworkFormatBitmap(call.argument("format")!!) + var quality = call.argument("quality")!! + if (quality > 100) quality = 100 + try { + bitmap.compress(format, quality, byteArrayBase) + } catch (e: Exception) { + Log.i("on_audio_error", "$e") + } + convertedBytes = byteArrayBase.toByteArray() + byteArrayBase.close() + return convertedBytes + } } diff --git a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/extensions/OnHelperExtension.kt b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/extensions/OnHelperExtension.kt new file mode 100644 index 0000000..a0eecb0 --- /dev/null +++ b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/extensions/OnHelperExtension.kt @@ -0,0 +1,32 @@ +package com.lucasjosino.on_audio_edit.extensions + +fun String.tryInt(key: Int): Any? { + return when (key) { + 1, + 6, + 12, + 25, + 36, + 43, + 44, + 45, + 46, + 47, + 49, + 51, + 53, + 54, + 55, + 60, + 61, + 63, + 69, + 72, + 73, + 75, + 78, + 80, + 81 -> this.toIntOrNull() + else -> key + } +} diff --git a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/methods/read/OnAudioRead.kt b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/methods/read/OnAudioRead.kt index cf74913..5c1842b 100644 --- a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/methods/read/OnAudioRead.kt +++ b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/methods/read/OnAudioRead.kt @@ -1,10 +1,10 @@ package com.lucasjosino.on_audio_edit.methods.read +import com.lucasjosino.on_audio_edit.extensions.tryInt import com.lucasjosino.on_audio_edit.types.checkTag import com.lucasjosino.on_audio_edit.utils.checkAndGetExtraInfo import com.lucasjosino.on_audio_edit.utils.getAllProjection import com.lucasjosino.on_audio_edit.utils.getExtraInfo -import com.lucasjosino.on_audio_edit.utils.getProjection import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import org.jaudiotagger.audio.AudioFileIO @@ -24,8 +24,13 @@ class OnAudioRead { val audioTag = audioFile.tag // Getting all tags - val tagsData: MutableMap = HashMap() - for (tag in getAllProjection()) tagsData[tag.name] = audioTag.getValue(tag, 0).orEmpty() + val tagsData: MutableMap = HashMap() + for (tag in getAllProjection()) { + val value = audioTag.getValue(tag, 0) + if (!value.isNullOrEmpty()) { + tagsData[tag.name] = value.tryInt(tag.ordinal) + } + } // Extra information tagsData.putAll(checkAndGetExtraInfo(audioFile)) @@ -40,7 +45,7 @@ class OnAudioRead { val data: ArrayList = call.argument("data")!! // Getting all path - val tagsList: ArrayList> = ArrayList() + val tagsList: ArrayList> = ArrayList() // Looping until get the last path for (pathData in data) { @@ -50,8 +55,13 @@ class OnAudioRead { val audioTag = audioFile.tag // Getting all tags - val tagsData: MutableMap = HashMap() - for (tag in getProjection()) tagsData[tag.name] = audioTag.getValue(tag, 0).orEmpty() + val tagsData: MutableMap = HashMap() + for (tag in getAllProjection()) { + val value = audioTag.getValue(tag, 0) + if (!value.isNullOrEmpty()) { + tagsData[tag.name] = value.tryInt(tag.ordinal) + } + } // Extra information tagsData.putAll(checkAndGetExtraInfo(audioFile)) @@ -75,14 +85,19 @@ class OnAudioRead { // Getting specific tag val resultTag = when (tag) { - 4 -> audioFile.audioHeader.bitRate - 5 -> audioFile.audioHeader.channels - 9 -> audioTag.firstArtwork.binaryData.toString() - 10 -> audioFile.audioHeader.format - 15 -> audioFile.audioHeader.trackLength.toString() - 25 -> audioFile.audioHeader.sampleRate - 31 -> audioFile.audioHeader.encodingType - else -> audioTag.getValue(checkTag(tag), 0).orEmpty() + 82 -> audioFile.audioHeader.bitRate.toInt() + 83 -> audioFile.audioHeader.channels + 84 -> audioTag.firstArtwork.binaryData + 85 -> audioFile.audioHeader.format + 86 -> audioFile.audioHeader.trackLength + 87 -> audioFile.audioHeader.sampleRate.toInt() + 88 -> audioFile.audioHeader.encodingType + else -> { + val value = audioTag.getValue(checkTag(tag), 0) + if (!value.isNullOrEmpty()) { + value.tryInt(tag) + } else null + } } // Sending to Dart diff --git a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/types/TagType.kt b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/types/TagType.kt index 3e00dfe..3f07364 100644 --- a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/types/TagType.kt +++ b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/types/TagType.kt @@ -2,42 +2,90 @@ package com.lucasjosino.on_audio_edit.types import org.jaudiotagger.tag.FieldKey -fun checkTag(tag: Int) : FieldKey? { +fun checkTag(tag: Int): FieldKey? { return when (tag) { - 0 -> FieldKey.ALBUM - 1 -> FieldKey.ALBUM_ARTIST - 2 -> FieldKey.ARTIST - 3 -> FieldKey.ARTISTS - 4 -> FieldKey.BPM -// FieldKey.BITRATE, 5 -// FieldKey.CHANNELS, 6 - 7 -> FieldKey.COMPOSER - 8 -> FieldKey.COUNTRY - 9 -> FieldKey.COVER_ART -// FieldKey.FIRST_ARTWORK, 10 -// FieldKey.FORMAT, 11 - 12 -> FieldKey.GENRE - 13 -> FieldKey.ISRC - 14 -> FieldKey.KEY - 15 -> FieldKey.LANGUAGE -// FieldKey.LENGTH, 16 - 17 -> FieldKey.LYRICS - 18 -> FieldKey.ORIGINAL_ALBUM - 19 -> FieldKey.ORIGINAL_ARTIST - 20 -> FieldKey.ORIGINAL_LYRICIST - 21 -> FieldKey.ORIGINAL_YEAR - 22 -> FieldKey.PRODUCER - 23 -> FieldKey.QUALITY - 24-> FieldKey.RATING - 25 -> FieldKey.RECORD_LABEL -// FieldKey.SAMPLE_RATE, 26 - 27 -> FieldKey.SUBTITLE - 28 -> FieldKey.TAGS - 29 -> FieldKey.TEMPO - 30 -> FieldKey.TITLE - 31 -> FieldKey.TRACK -// FieldKey.TYPE, 32 - 33 -> FieldKey.YEAR + 0 -> FieldKey.ACOUSTID_FINGERPRINT // 0 + 1 -> FieldKey.ACOUSTID_ID // 1 + 2 -> FieldKey.ALBUM // 2 + 3 -> FieldKey.ALBUM_ARTIST // 3 + 4 -> FieldKey.ALBUM_ARTIST_SORT // 4 + 5 -> FieldKey.ALBUM_SORT // 5 + 6 -> FieldKey.AMAZON_ID // 6 + 7 -> FieldKey.ARRANGER // 7 + 8 -> FieldKey.ARTIST // 8 + 9 -> FieldKey.ARTIST_SORT // 9 + 10 -> FieldKey.ARTISTS // 10 + 11 -> FieldKey.BARCODE // 11 + 12 -> FieldKey.BPM // 12 + 13 -> FieldKey.CATALOG_NO // 13 + 14 -> FieldKey.COMMENT // 14 + 15 -> FieldKey.COMPOSER // 15 + 16 -> FieldKey.COMPOSER_SORT // 16 + 17 -> FieldKey.CONDUCTOR // 17 + 18 -> FieldKey.COUNTRY // 18 + 19 -> FieldKey.COVER_ART // 19 + 20 -> FieldKey.CUSTOM1 // 20 + 21 -> FieldKey.CUSTOM2 // 21 + 22 -> FieldKey.CUSTOM3 // 22 + 23 -> FieldKey.CUSTOM4 // 23 + 24 -> FieldKey.CUSTOM5 // 24 + 25 -> FieldKey.DISC_NO // 25 + 26 -> FieldKey.DISC_SUBTITLE // 26 + 27 -> FieldKey.DISC_TOTAL // 27 + 28 -> FieldKey.DJMIXER // 28 + 29 -> FieldKey.ENCODER // 29 + 30 -> FieldKey.ENGINEER // 30 + 31 -> FieldKey.FBPM // 31 + 32 -> FieldKey.GENRE // 32 + 33 -> FieldKey.GROUPING // 33 + 34 -> FieldKey.ISRC // 34 + 35 -> FieldKey.IS_COMPILATION // 35 + 36 -> FieldKey.KEY // 36 + 37 -> FieldKey.LANGUAGE // 37 + 38 -> FieldKey.LYRICIST // 38 + 39 -> FieldKey.LYRICS // 39 + 40 -> FieldKey.MEDIA // 40 + 41 -> FieldKey.MIXER // 41 + 42 -> FieldKey.MOOD // 42 + 43 -> FieldKey.MUSICBRAINZ_ARTISTID // 43 + 44 -> FieldKey.MUSICBRAINZ_DISC_ID // 44 + 45 -> FieldKey.MUSICBRAINZ_ORIGINAL_RELEASE_ID // 45 + 46 -> FieldKey.MUSICBRAINZ_RELEASEARTISTID // 46 + 47 -> FieldKey.MUSICBRAINZ_RELEASEID // 47 + 48 -> FieldKey.MUSICBRAINZ_RELEASE_COUNTRY // 48 + 49 -> FieldKey.MUSICBRAINZ_RELEASE_GROUP_ID // 49 + 50 -> FieldKey.MUSICBRAINZ_RELEASE_STATUS // 50 + 51 -> FieldKey.MUSICBRAINZ_RELEASE_TRACK_ID // 51 + 52 -> FieldKey.MUSICBRAINZ_RELEASE_TYPE // 52 + 53 -> FieldKey.MUSICBRAINZ_TRACK_ID // 53 + 54 -> FieldKey.MUSICBRAINZ_WORK_ID // 54 + 55 -> FieldKey.MUSICIP_ID // 55 + 56 -> FieldKey.OCCASION // 56 + 57 -> FieldKey.ORIGINAL_ALBUM // 57 + 58 -> FieldKey.ORIGINAL_ARTIST // 58 + 59 -> FieldKey.ORIGINAL_LYRICIST // 59 + 60 -> FieldKey.ORIGINAL_YEAR // 60 + 61 -> FieldKey.QUALITY // 61 + 62 -> FieldKey.PRODUCER // 62 + 63 -> FieldKey.RATING // 63 + 64 -> FieldKey.RECORD_LABEL // 64 + 65 -> FieldKey.REMIXER // 65 + 66 -> FieldKey.SCRIPT // 66 + 67 -> FieldKey.SUBTITLE // 67 + 68 -> FieldKey.TAGS // 68 + 69 -> FieldKey.TEMPO // 69 + 70 -> FieldKey.TITLE // 70 + 71 -> FieldKey.TITLE_SORT // 71 + 72 -> FieldKey.TRACK // 72 + 73 -> FieldKey.TRACK_TOTAL // 73 + 74 -> FieldKey.URL_DISCOGS_ARTIST_SITE // 74 + 75 -> FieldKey.URL_DISCOGS_RELEASE_SITE // 75 + 76 -> FieldKey.URL_LYRICS_SITE // 76 + 77 -> FieldKey.URL_OFFICIAL_ARTIST_SITE // 77 + 78 -> FieldKey.URL_OFFICIAL_RELEASE_SITE // 78 + 79 -> FieldKey.URL_WIKIPEDIA_ARTIST_SITE // 79 + 80 -> FieldKey.URL_WIKIPEDIA_RELEASE_SITE // 80 + 81 -> FieldKey.YEAR // 81 else -> null } } \ No newline at end of file diff --git a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/utils/OnArtworkFormat.kt b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/utils/OnArtworkFormat.kt index 3856692..6fb10e0 100644 --- a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/utils/OnArtworkFormat.kt +++ b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/utils/OnArtworkFormat.kt @@ -1,13 +1,20 @@ package com.lucasjosino.on_audio_edit.utils +import android.graphics.Bitmap import org.jaudiotagger.tag.id3.valuepair.ImageFormats import java.lang.Exception fun checkArtworkFormat(value: Int) : String { return when (value) { - 0 -> ImageFormats.MIME_TYPE_JPG - 1 -> ImageFormats.MIME_TYPE_JPEG - 2 -> ImageFormats.MIME_TYPE_PNG + 0 -> ImageFormats.MIME_TYPE_JPEG + 1 -> ImageFormats.MIME_TYPE_PNG else -> throw Exception("[ImageFormats] don't exist") } -} \ No newline at end of file +} +fun checkArtworkFormatBitmap(format: Int) : Bitmap.CompressFormat { + return when (format) { + 0 -> Bitmap.CompressFormat.JPEG + 1 -> Bitmap.CompressFormat.PNG + else -> throw Exception("[checkArtworkFormat] value don't exist!") + } +} diff --git a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/utils/OnExtraInfo.kt b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/utils/OnExtraInfo.kt index f96f06d..33ce960 100644 --- a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/utils/OnExtraInfo.kt +++ b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/utils/OnExtraInfo.kt @@ -3,27 +3,27 @@ package com.lucasjosino.on_audio_edit.utils import org.jaudiotagger.audio.AudioFile import org.jaudiotagger.tag.Tag -fun checkAndGetExtraInfo(audioFile: AudioFile) : MutableMap { - val extraInfo: MutableMap = HashMap() - extraInfo["BITRATE"] = audioFile.audioHeader.bitRate +fun checkAndGetExtraInfo(audioFile: AudioFile) : MutableMap { + val extraInfo: MutableMap = HashMap() + extraInfo["BITRATE"] = audioFile.audioHeader.bitRate.toInt() extraInfo["FORMAT"] = audioFile.audioHeader.format - extraInfo["SAMPLE_RATE"] = audioFile.audioHeader.sampleRate + extraInfo["SAMPLE_RATE"] = audioFile.audioHeader.sampleRate.toInt() extraInfo["CHANNELS"] = audioFile.audioHeader.channels extraInfo["TYPE"] = audioFile.audioHeader.encodingType - extraInfo["LENGTH"] = audioFile.file.length().toString() - extraInfo["FIRST_ARTWORK"] = audioFile.tag.firstArtwork.binaryData.toString() + extraInfo["LENGTH"] = audioFile.file.length() + extraInfo["FIRST_ARTWORK"] = audioFile.tag.firstArtwork.binaryData return extraInfo } -fun getExtraInfo(audioFile: AudioFile, value: Int, audioTag: Tag) : String { +fun getExtraInfo(audioFile: AudioFile, value: Int, audioTag: Tag) : Any? { return when (value) { - 4 -> audioFile.audioHeader.bitRate - 5 -> audioFile.audioHeader.channels - 9 -> audioTag.firstArtwork.binaryData.toString() - 10 -> audioFile.audioHeader.format - 15 -> audioFile.audioHeader.trackLength.toString() - 25 -> audioFile.audioHeader.sampleRate - 31 -> audioFile.audioHeader.encodingType + 82 -> audioFile.audioHeader.bitRate.toInt() + 83 -> audioFile.audioHeader.channels + 84 -> audioTag.firstArtwork.binaryData + 85 -> audioFile.audioHeader.format + 86 -> audioFile.audioHeader.trackLength + 87 -> audioFile.audioHeader.sampleRate.toInt() + 88 -> audioFile.audioHeader.encodingType else -> throw Exception("[getExtraInfo] gave a value that don't exist!") } } \ No newline at end of file diff --git a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/utils/OnTagsProjections.kt b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/utils/OnTagsProjections.kt index fdef4b2..4b5fe33 100644 --- a/android/src/main/kotlin/com/lucasjosino/on_audio_edit/utils/OnTagsProjections.kt +++ b/android/src/main/kotlin/com/lucasjosino/on_audio_edit/utils/OnTagsProjections.kt @@ -2,188 +2,89 @@ package com.lucasjosino.on_audio_edit.utils import org.jaudiotagger.tag.FieldKey -fun getProjection() : Array = projection fun getAllProjection() : Array = allProjection -private var projection = arrayOf( - FieldKey.ALBUM_ARTIST, - FieldKey.ARTIST, - FieldKey.ARTISTS, - FieldKey.BPM, -// FieldKey.BITRATE, 4 -// FieldKey.CHANNELS, 5 - FieldKey.COMPOSER, - FieldKey.COUNTRY, - FieldKey.COVER_ART, -// FieldKey.FIRST_ARTWORK, 9 -// FieldKey.FORMAT, 10 - FieldKey.GENRE, - FieldKey.ISRC, - FieldKey.KEY, - FieldKey.LANGUAGE, -// FieldKey.LENGTH, 15 - FieldKey.LYRICS, - FieldKey.ORIGINAL_ALBUM, - FieldKey.ORIGINAL_ARTIST, - FieldKey.ORIGINAL_LYRICIST, - FieldKey.ORIGINAL_YEAR, - FieldKey.PRODUCER, - FieldKey.QUALITY, - FieldKey.RATING, - FieldKey.RECORD_LABEL, -// FieldKey.SAMPLE_RATE, 25 - FieldKey.SUBTITLE, - FieldKey.TAGS, - FieldKey.TEMPO, - FieldKey.TITLE, - FieldKey.TRACK, -// FieldKey.TYPE, 31 - FieldKey.YEAR -) private var allProjection = arrayOf( - FieldKey.ACOUSTID_FINGERPRINT, - FieldKey.ACOUSTID_ID, - FieldKey.ALBUM, - FieldKey.ALBUM_ARTIST, - FieldKey.ALBUM_ARTIST_SORT, - FieldKey.ALBUM_SORT, - FieldKey.AMAZON_ID, - FieldKey.ARRANGER, - FieldKey.ARTIST, - FieldKey.ARTIST_SORT, - FieldKey.ARTISTS, - FieldKey.BARCODE, - FieldKey.BPM, - FieldKey.CATALOG_NO, - FieldKey.COMMENT, - FieldKey.COMPOSER, - FieldKey.COMPOSER_SORT, - FieldKey.CONDUCTOR, - FieldKey.COUNTRY, - FieldKey.COVER_ART, - FieldKey.CUSTOM1, - FieldKey.CUSTOM2, - FieldKey.CUSTOM3, - FieldKey.CUSTOM4, - FieldKey.CUSTOM5, - FieldKey.DISC_NO, - FieldKey.DISC_SUBTITLE, - FieldKey.DISC_TOTAL, - FieldKey.DJMIXER, - FieldKey.ENCODER, - FieldKey.ENGINEER, - FieldKey.FBPM, - FieldKey.GENRE, - FieldKey.GROUPING, - FieldKey.ISRC, - FieldKey.IS_COMPILATION, - FieldKey.KEY, - FieldKey.LANGUAGE, - FieldKey.LYRICIST, - FieldKey. LYRICS, - FieldKey.MEDIA, - FieldKey.MIXER, - FieldKey.MOOD, - FieldKey.MUSICBRAINZ_ARTISTID, - FieldKey.MUSICBRAINZ_DISC_ID, - FieldKey.MUSICBRAINZ_ORIGINAL_RELEASE_ID, - FieldKey.MUSICBRAINZ_RELEASEARTISTID, - FieldKey.MUSICBRAINZ_RELEASEID, - FieldKey.MUSICBRAINZ_RELEASE_COUNTRY, - FieldKey.MUSICBRAINZ_RELEASE_GROUP_ID, - FieldKey.MUSICBRAINZ_RELEASE_STATUS, - FieldKey.MUSICBRAINZ_RELEASE_TRACK_ID, - FieldKey.MUSICBRAINZ_RELEASE_TYPE, - FieldKey.MUSICBRAINZ_TRACK_ID, - FieldKey.MUSICBRAINZ_WORK_ID, - FieldKey.MUSICIP_ID, - FieldKey.OCCASION, - FieldKey.ORIGINAL_ALBUM, - FieldKey.ORIGINAL_ARTIST, - FieldKey.ORIGINAL_LYRICIST, - FieldKey.ORIGINAL_YEAR, - FieldKey.QUALITY, - FieldKey.PRODUCER, - FieldKey.RATING, - FieldKey.RECORD_LABEL, - FieldKey.REMIXER, - FieldKey.SCRIPT, - FieldKey.SUBTITLE, - FieldKey.TAGS, - FieldKey.TEMPO, - FieldKey.TITLE, - FieldKey.TITLE_SORT, - FieldKey.TRACK, - FieldKey.TRACK_TOTAL, - FieldKey.URL_DISCOGS_ARTIST_SITE, - FieldKey.URL_DISCOGS_RELEASE_SITE, - FieldKey.URL_LYRICS_SITE, - FieldKey.URL_OFFICIAL_ARTIST_SITE, - FieldKey.URL_OFFICIAL_RELEASE_SITE, - FieldKey.URL_WIKIPEDIA_ARTIST_SITE, - FieldKey.URL_WIKIPEDIA_RELEASE_SITE, - FieldKey.YEAR -) - -// Extras: - -// All Tags -// { -// ACOUSTID_FINGERPRINT, -// ACOUSTID_ID, -// ALBUM, -// ALBUM_ARTIST_SORT, -// ALBUM_SORT, -// AMAZON_ID, -// ARRANGER, -// ARTIST_SORT, -// BARCODE, -// CATALOG_NO, -// COMMENT, -// COMPOSER_SORT, -// CONDUCTOR, -// COVER_ART, -// CUSTOM1, -// CUSTOM2, -// CUSTOM3, -// CUSTOM4, -// CUSTOM5, -// DISC_NO, -// DISC_SUBTITLE, -// DISC_TOTAL, -// DJMIXER, -// ENCODER, -// ENGINEER, -// FBPM, -// GROUPING, -// IS_COMPILATION, -// LYRICIST, -// MEDIA, -// MIXER, -// MOOD, -// MUSICBRAINZ_ARTISTID, -// MUSICBRAINZ_DISC_ID, -// MUSICBRAINZ_ORIGINAL_RELEASE_ID, -// MUSICBRAINZ_RELEASEARTISTID, -// MUSICBRAINZ_RELEASEID, -// MUSICBRAINZ_RELEASE_COUNTRY, -// MUSICBRAINZ_RELEASE_GROUP_ID, -// MUSICBRAINZ_RELEASE_STATUS, -// MUSICBRAINZ_RELEASE_TRACK_ID, -// MUSICBRAINZ_RELEASE_TYPE, -// MUSICBRAINZ_TRACK_ID, -// MUSICBRAINZ_WORK_ID, -// MUSICIP_ID, -// OCCASION, -// REMIXER, -// SCRIPT, -// TITLE_SORT, -// TRACK_TOTAL, -// URL_DISCOGS_ARTIST_SITE, -// URL_DISCOGS_RELEASE_SITE, -// URL_LYRICS_SITE, -// URL_OFFICIAL_ARTIST_SITE, -// URL_OFFICIAL_RELEASE_SITE, -// URL_WIKIPEDIA_ARTIST_SITE, -// URL_WIKIPEDIA_RELEASE_SITE, -// } \ No newline at end of file + FieldKey.ACOUSTID_FINGERPRINT, // 0 + FieldKey.ACOUSTID_ID, // 1 + FieldKey.ALBUM, // 2 + FieldKey.ALBUM_ARTIST, // 3 + FieldKey.ALBUM_ARTIST_SORT, // 4 + FieldKey.ALBUM_SORT, // 5 + FieldKey.AMAZON_ID, // 6 + FieldKey.ARRANGER, // 7 + FieldKey.ARTIST, // 8 + FieldKey.ARTIST_SORT, // 9 + FieldKey.ARTISTS, // 10 + FieldKey.BARCODE, // 11 + FieldKey.BPM, // 12 + FieldKey.CATALOG_NO, // 13 + FieldKey.COMMENT, // 14 + FieldKey.COMPOSER, // 15 + FieldKey.COMPOSER_SORT, // 16 + FieldKey.CONDUCTOR, // 17 + FieldKey.COUNTRY, // 18 + FieldKey.COVER_ART, // 19 + FieldKey.CUSTOM1, // 20 + FieldKey.CUSTOM2, // 21 + FieldKey.CUSTOM3, // 22 + FieldKey.CUSTOM4, // 23 + FieldKey.CUSTOM5, // 24 + FieldKey.DISC_NO, // 25 + FieldKey.DISC_SUBTITLE, // 26 + FieldKey.DISC_TOTAL, // 27 + FieldKey.DJMIXER, // 28 + FieldKey.ENCODER, // 29 + FieldKey.ENGINEER, // 30 + FieldKey.FBPM, // 31 + FieldKey.GENRE, // 32 + FieldKey.GROUPING, // 33 + FieldKey.ISRC, // 34 + FieldKey.IS_COMPILATION, // 35 + FieldKey.KEY, // 36 + FieldKey.LANGUAGE, // 37 + FieldKey.LYRICIST, // 38 + FieldKey.LYRICS, // 39 + FieldKey.MEDIA, // 40 + FieldKey.MIXER, // 41 + FieldKey.MOOD, // 42 + FieldKey.MUSICBRAINZ_ARTISTID, // 43 + FieldKey.MUSICBRAINZ_DISC_ID, // 44 + FieldKey.MUSICBRAINZ_ORIGINAL_RELEASE_ID, // 45 + FieldKey.MUSICBRAINZ_RELEASEARTISTID, // 46 + FieldKey.MUSICBRAINZ_RELEASEID, // 47 + FieldKey.MUSICBRAINZ_RELEASE_COUNTRY, // 48 + FieldKey.MUSICBRAINZ_RELEASE_GROUP_ID, // 49 + FieldKey.MUSICBRAINZ_RELEASE_STATUS, // 50 + FieldKey.MUSICBRAINZ_RELEASE_TRACK_ID, // 51 + FieldKey.MUSICBRAINZ_RELEASE_TYPE, // 52 + FieldKey.MUSICBRAINZ_TRACK_ID, // 53 + FieldKey.MUSICBRAINZ_WORK_ID, // 54 + FieldKey.MUSICIP_ID, // 55 + FieldKey.OCCASION, // 56 + FieldKey.ORIGINAL_ALBUM, // 57 + FieldKey.ORIGINAL_ARTIST, // 58 + FieldKey.ORIGINAL_LYRICIST, // 59 + FieldKey.ORIGINAL_YEAR, // 60 + FieldKey.QUALITY, // 61 + FieldKey.PRODUCER, // 62 + FieldKey.RATING, // 63 + FieldKey.RECORD_LABEL, // 64 + FieldKey.REMIXER, // 65 + FieldKey.SCRIPT, // 66 + FieldKey.SUBTITLE, // 67 + FieldKey.TAGS, // 68 + FieldKey.TEMPO, // 69 + FieldKey.TITLE, // 70 + FieldKey.TITLE_SORT, // 71 + FieldKey.TRACK, // 72 + FieldKey.TRACK_TOTAL, // 73 + FieldKey.URL_DISCOGS_ARTIST_SITE, // 74 + FieldKey.URL_DISCOGS_RELEASE_SITE, // 75 + FieldKey.URL_LYRICS_SITE, // 76 + FieldKey.URL_OFFICIAL_ARTIST_SITE, // 77 + FieldKey.URL_OFFICIAL_RELEASE_SITE, // 78 + FieldKey.URL_WIKIPEDIA_ARTIST_SITE, // 79 + FieldKey.URL_WIKIPEDIA_RELEASE_SITE, // c + FieldKey.YEAR // 81 +) \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index 77803b0..7042b51 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -12,8 +12,6 @@ Copyright: © 2021, Lucas Josino. All rights reserved. ============= */ -import 'dart:developer'; - import 'package:flutter/material.dart'; import 'package:on_audio_edit/on_audio_edit.dart'; import 'package:on_audio_query/on_audio_query.dart'; @@ -104,11 +102,11 @@ class _MyAppState extends State with SingleTickerProviderStateMixin { onTap: () => optionsDialog(context, index), // [onLongPress] will read all information about selected items: onLongPress: () async { - var result = await _audioEdit.readAllAudio( + var result = await _audioEdit.readAudio( songList[index].data, ); // Print the result. - log(result.toString()); + debugPrint('$result'); }, title: Text(songList[index].title), subtitle: Text( diff --git a/lib/details/models/audio_model.dart b/lib/details/models/audio_model.dart index b8e4b71..d3f7e20 100644 --- a/lib/details/models/audio_model.dart +++ b/lib/details/models/audio_model.dart @@ -7,111 +7,272 @@ class AudioModel { /// The type dynamic is used for both but, the map is always based in [String, dynamic] final Map _info; + /// Return song [acoustIdFingerPrint] + String? get acoustIdFingerPrint => _info["ACOUSTID_FINGERPRINT"]; + + /// Return song [acoistIdID] + int? get acoistIdID => _info["ACOUSTID_ID"]; + + /// Return song [album] + String? get album => _info["ALBUM"]; + /// Return song [albumArtist] - String get albumArtist => _info["ALBUM_ARTIST"]; + String? get albumArtist => _info["ALBUM_ARTIST"]; + + /// Return song [albumArtistSort] + String? get albumArtistSort => _info["ALBUM_ARTIST_SORT"]; + + /// Return song [albumSort] + String? get albumSort => _info["ALBUM_SORT"]; + + /// Return song [amazonId] + String? get amazonId => _info["AMAZON_ID"]; + + /// Return song [arranger] + String? get arranger => _info["ARRANGER"]; /// Return song [artist] - String get artist => _info["ARTIST"]; + String? get artist => _info["ARTIST"]; + + /// Return song [artistSort] + String? get artistSort => _info["ARTIST_SORT"]; + + /// Return song [artists] + String? get artists => _info["ARTISTS"]; /// Return song [artists] - String get artists => _info["ARTISTS"]; + String? get barcode => _info["BARCODE"]; /// Return song [beatsPerMinutes] - String get beatsPerMinutes => _info["BEATS_PER_MINUTE"]; + int? get beatsPerMinutes => _info["BEATS_PER_MINUTE"]; - /// Return song [bitrate] - String get bitrate => _info["BITRATE"]; + /// Return song [caralogNo] + int? get caralogNo => _info["CATALOG_NO"]; - /// Return song [channels] - String get channels => _info["CHANNELS"]; + /// Return song [comment] + int? get comment => _info["COMMENT"]; /// Return song [composer] - String get composer => _info["COMPOSER"]; + String? get composer => _info["COMPOSER"]; - /// Return song [country] - String get country => _info["COUNTRY"]; + /// Return song [composerSort] + String? get composerSort => _info["COMPOSER_SORT"]; + + /// Return song [conductor] + String? get conductor => _info["CONDUCTOR"]; /// Return song [country] - String get coverArt => _info["COVER_ART"]; + String? get country => _info["COUNTRY"]; /// Return song [country] - String get firstArtwork => _info["FIRST_ARTWORK"]; + String? get coverArt => _info["COVER_ART"]; - /// Return song [format] - String get format => _info["FORMAT"]; + /// Return song [discNo] + String? get discNo => _info["DISC_NO"]; + + /// Return song [discSubtitle] + String? get discSubtitle => _info["DISC_SUBTITLE"]; + + /// Return song [discTotal] + String? get discTotal => _info["DISC_TOTAL"]; + + /// Return song [djMixer] + String? get djMixer => _info["DJMIXER"]; + + /// Return song [encoder] + String? get encoder => _info["ENCODER"]; + + /// Return song [engineer] + String? get engineer => _info["ENGINEER"]; + + /// Return song [fbmp] + String? get fbmp => _info["FBPM"]; /// Return song [genre] - String get genre => _info["GENRE"]; + String? get genre => _info["GENRE"]; - /// Return song [id] - String get id => _info["ID"]; + /// Return song [grouping] + String? get grouping => _info["GROUPING"]; + + /// Deprecated after [1.2.0] + @Deprecated("This method will be removed soon") + int? get id => _info["ID"]; /// Return song [isrc] - String get isrc => _info["ISRC"]; + String? get isrc => _info["ISRC"]; + + /// Return song [isCompilation] + String? get isCompilation => _info["IS_COMPILATION"]; /// Return song [key] - String get key => _info["KEY"]; + int? get key => _info["KEY"]; /// Return song [language] - String get language => _info["LANGUAGE"]; + String? get language => _info["LANGUAGE"]; - /// Return song [length] - String get length => _info["LENGTH"]; + /// Return song [lyricist] + String? get lyricist => _info["LYRICIST"]; /// Return song [lyrics] - String get lyrics => _info["LYRICS"]; + String? get lyrics => _info["LYRICS"]; + + /// Return song [media] + String? get media => _info["MEDIA"]; + + /// Return song [mixer] + String? get mixer => _info["MIXER"]; + + /// Return song [mood] + String? get mood => _info["MOOD"]; + + /// Return song [musicBrainzArtistId] + int? get musicBrainzArtistId => _info["MUSICBRAINZ_ARTISTID"]; + + /// Return song [musicBrainzDiscId] + int? get musicBrainzDiscId => _info["MUSICBRAINZ_DISC_ID"]; + + /// Return song [musicBrainzOriginalReleaseId] + int? get musicBrainzOriginalReleaseId => + _info["MUSICBRAINZ_ORIGINAL_RELEASE_ID"]; + + /// Return song [musicBrainzReleaseArtistId] + int? get musicBrainzReleaseArtistId => _info["MUSICBRAINZ_RELEASEARTISTID"]; + + /// Return song [musicBrainzReleaseId] + int? get musicBrainzReleaseId => _info["MUSICBRAINZ_RELEASEID"]; + + /// Return song [musicBrainzReleaseCountry] + String? get musicBrainzReleaseCountry => _info["MUSICBRAINZ_RELEASE_COUNTRY"]; + + /// Return song [musicBrainzReleaseGroupId] + int? get musicBrainzReleaseGroupId => _info["MUSICBRAINZ_RELEASE_GROUP_ID"]; + + /// Return song [musicBrainzReleaseStatus] + String? get musicBrainzReleaseStatus => _info["MUSICBRAINZ_RELEASE_STATUS"]; + + /// Return song [musicBrainzReleaseTrackId] + int? get musicBrainzReleaseTrackId => _info["MUSICBRAINZ_RELEASE_TRACK_ID"]; + + /// Return song [musicBrainzReleaseType] + String? get musicBrainzReleaseType => _info["MUSICBRAINZ_RELEASE_TYPE"]; + + /// Return song [musicBrainzTrackId] + int? get musicBrainzTrackId => _info["MUSICBRAINZ_TRACK_ID"]; + + /// Return song [musicBrainzWorkId] + int? get musicBrainzWorkId => _info["MUSICBRAINZ_WORK_ID"]; + + /// Return song [musiCIPID] + int? get musiCIPID => _info["MUSICIP_ID"]; + + /// Return song [occasion] + int? get occasion => _info["OCCASION"]; /// Return song [originalAlbum] - String get originalAlbum => _info["ORIGINAL_ALBUM"]; + String? get originalAlbum => _info["ORIGINAL_ALBUM"]; /// Return song [originalAlbum] - String get originalArtist => _info["ORIGINAL_ARTIST"]; + String? get originalArtist => _info["ORIGINAL_ARTIST"]; /// Return song [originalLyricist] - String get originalLyricist => _info["ORIGINAL_LYRICIST"]; + String? get originalLyricist => _info["ORIGINAL_LYRICIST"]; /// Return song [originalYear] - String get originalYear => _info["ORIGINAL_YEAR"]; - - /// Return song [producer] - String get producer => _info["PRODUCER"]; + int? get originalYear => _info["ORIGINAL_YEAR"]; /// Return song [quality] - String get quality => _info["QUALITY"]; + int? get quality => _info["QUALITY"]; + + /// Return song [producer] + String? get producer => _info["PRODUCER"]; /// Return song [rating] - String get rating => _info["RATING"]; + int? get rating => _info["RATING"]; /// Return song [recordLabel] - String get recordLabel => _info["RECORD_LABEL"]; + String? get recordLabel => _info["RECORD_LABEL"]; + + /// Return song [remixer] + String? get remixer => _info["REMIXER"]; - /// Return song [sampleRate] - String get sampleRate => _info["SAMPLE_RATE"]; + /// Return song [remixer] + String? get script => _info["SCRIPT"]; /// Return song [subTitle] - String get subTitle => _info["SUBTITLE"]; + String? get subTitle => _info["SUBTITLE"]; /// Return song [tags] - String get tags => _info["TAGS"]; + String? get tags => _info["TAGS"]; /// Return song [tempo] - String get tempo => _info["TEMPO"]; + int? get tempo => _info["TEMPO"]; /// Return song [title] String get title => _info["TITLE"]; + /// Return song [titleSort] + String? get titleSort => _info["TITLE_SORT"]; + /// Return song [track] - String get track => _info["TRACK"]; + int? get track => _info["TRACK"]; + + /// Return song [trackTotal] + int? get trackTotal => _info["TRACK_TOTAL"]; + + /// Return song [urlDiscOGSArtistSite] + String? get urlDiscOGSArtistSite => _info["URL_DISCOGS_ARTIST_SITE"]; + + /// Return song [urlDiscOGSReleaseSite] + String? get urlDiscOGSReleaseSite => _info["URL_DISCOGS_RELEASE_SITE"]; + + /// Return song [urlLyricsSite] + String? get urlLyricsSite => _info["URL_LYRICS_SITE"]; + + /// Return song [urlOfficialArtistSite] + String? get urlOfficialArtistSite => _info["URL_OFFICIAL_ARTIST_SITE"]; - /// Return song [type] - String get type => _info["TYPE"]; + /// Return song [urlOfficialReleaseSite] + String? get urlOfficialReleaseSite => _info["URL_OFFICIAL_RELEASE_SITE"]; + + /// Return song [urlWikipediaArtistSite] + String? get urlWikipediaArtistSite => _info["URL_WIKIPEDIA_ARTIST_SITE"]; + + /// Return song [urlWikipediaReleaseSite] + String? get urlWikipediaReleaseSite => _info["URL_WIKIPEDIA_RELEASE_SITE"]; /// Return song [year] - String get year => _info["YEAR"]; + String? get year => _info["YEAR"]; + + // + + /// Return song [bitrate] + int? get bitrate => _info["BITRATE"]; + + /// Return song [channels] + String? get channels => _info["CHANNELS"]; + + /// Return song [country] + Uint8List? get firstArtwork => _info["FIRST_ARTWORK"]; + + /// Return song [format] + String? get format => _info["FORMAT"]; + + /// Return song [length] + int? get length => _info["LENGTH"]; + + /// Return song [format] + int? get sampleRate => _info["SAMPLE_RATE"]; + + /// Return song [length] + String? get type => _info["TYPE"]; /// Return a map with all [keys] and [values] from specific song. Map get getMap => _info; @override - String toString() => _info.toString(); + String toString() { + var tmpInfo = _info; + tmpInfo.update("FIRST_ARTWORK", (value) => "${value.length} (Bytes)"); + return tmpInfo.toString(); + } } diff --git a/lib/details/models/image_model.dart b/lib/details/models/image_model.dart new file mode 100644 index 0000000..7b42900 --- /dev/null +++ b/lib/details/models/image_model.dart @@ -0,0 +1,25 @@ +part of on_audio_edit; + +/// [ImageModel] contains the image information. +class ImageModel { + ImageModel(this._info); + + /// The type dynamic is used for both but, the map is always based in [String, dynamic] + final Map _info; + + /// Return the image in bytes. + Uint8List get imageBytes => _info["imageBytes"]; + + /// Return the image data(path). + String get imageData => _info["imageData"]; + + /// Return a map with all [keys] and [values]. + Map get getMap => _info; + + @override + String toString() { + var tmpInfo = _info; + tmpInfo.update("imageBytes", (value) => "${value.length} (Bytes)"); + return tmpInfo.toString(); + } +} diff --git a/lib/details/on_audio_edit_controller.dart b/lib/details/on_audio_edit_controller.dart index bf24ed9..f710e19 100644 --- a/lib/details/on_audio_edit_controller.dart +++ b/lib/details/on_audio_edit_controller.dart @@ -20,20 +20,35 @@ class OnAudioEdit { static const String channelId = "com.lucasjosino.on_audio_edit"; static const MethodChannel _channel = MethodChannel(channelId); + /// Used to warn about tags that can't be modified. + void _cannotModifiy(TagType tag) { + switch (tag) { + case TagType.BITRATE: + case TagType.CHANNELS: + // case TagType.FIRST_ARTWORK: + case TagType.FORMAT: + case TagType.TRACK_LENGTH: + case TagType.SAMPLE_RATE: + case TagType.ENCODING_TYPE: + log('Cannot modifiy [$tag]. Ignoring request'); + break; + default: + break; + } + } + /// Used to return unique song info. /// /// Parameters: /// /// * [data] is used for find specific audio data. - /// * [searchInsideFolders] is used for find specific audio data even inside - /// the folders. **(Only required when using Android 10 or above)** /// /// Usage: /// /// ```dart /// AudioModel song = await OnAudioEdit().readAudio(data); /// String songTitle = song.title; - /// String songArtist = song.artist; + /// String songArtist = song.artist ?? ; /// ``` /// /// Important: @@ -41,13 +56,9 @@ class OnAudioEdit { /// * Calling any method without [READ] and [WRITE] permission will send a warning. /// /// Use [permissionsStatus] to see permissions status. - Future readAudio( - String data, { - bool searchInsideFolders = false, - }) async { + Future readAudio(String data) async { final Map resultReadAudio = await _channel.invokeMethod("readAudio", { "data": data, - "searchInsideFolders": searchInsideFolders, }); return AudioModel(resultReadAudio); } @@ -58,7 +69,7 @@ class OnAudioEdit { String data, { bool searchInsideFolders = false, }) async { - return readAudio(data, searchInsideFolders: searchInsideFolders); + return readAudio(data); } /// Used to return multiples songs info. @@ -70,10 +81,10 @@ class OnAudioEdit { /// Usage: /// /// ```dart - /// List AudiosTagModel> song = await OnAudioEdit().readAudios(allData); + /// List song = await OnAudioEdit().readAudios(allData); /// ... - /// var songInfo = song[0].title; - /// var songInfo2 = song[1].title; + /// String songInfo = song[0].title; + /// String songInfo2 = song[1].title; /// ``` /// /// Important: @@ -103,7 +114,7 @@ class OnAudioEdit { /// print(title); // Ex: Heavy, California /// ... /// String artist = await OnAudioEdit().readSingleAudioTag(data, TagsType.ARTIST); - /// print(artist); // Ex: Jungle + /// print(artist ?? ); // Ex: Jungle /// ``` /// /// Important: @@ -132,20 +143,19 @@ class OnAudioEdit { /// ```dart /// List tags = [ /// TagsType.TITLE, - /// TagsType.ARTIST + /// TagsType.ARTIST, /// ]; - /// var songSpecifics = await OnAudioEdit().readSpecificsAudioTags(data, tags); + /// AudioModel songSpecifics = await OnAudioEdit().readSpecificsAudioTags(data, tags); /// ... - /// var songTitle = songSpecifics["TITLE"]; - /// var songArtist = songSpecifics["ARTIST"]; + /// String songTitle = songSpecifics.title; + /// String songArtist = songSpecifics.artist ?? ; /// ``` /// /// Important: /// /// * Calling any method without [READ] and [WRITE] permission will throw a error. + /// /// Use [permissionsStatus] to see permissions status. - /// * Most audios have no information other than the [title] and [artist]. - /// * This method return a [Map]. Future readSpecificsAudioTags( String data, List tags) async { List tagsIndex = []; @@ -166,6 +176,8 @@ class OnAudioEdit { /// /// * [data] is used for find specific audio data. /// * [tags] is used to define what tags and values you want edit. + /// * [searchInsideFolders] is used for find specific audio data even inside + /// the folders. **(Only required when using Android 10 or above)** /// /// Usage: /// @@ -184,22 +196,29 @@ class OnAudioEdit { /// Use [permissionsStatus] to see permissions status. /// * This method return true if audio has edited or false if don't. /// - /// Super Important: + /// **Warning:** /// * This method works normal in Android below 10, /// from Android 10 or above user will need to accept access in Folder. /// You can see this "Complex Permission" status using [complexPermissionStatus]. - /// By default when calling this method will open a new screen to user choose a folder, + /// + /// * By default when calling this method will open a new screen to user choose a folder, /// but you can anticipate the request using [requestComplexPermission]. /// The request status and folder permission will be saved as persistent but /// if user uninstall the app, this permission will be removed. - Future editAudio(String data, Map tags) async { + Future editAudio( + String data, + Map tags, { + bool searchInsideFolders = false, + }) async { Map finalTags = {}; tags.forEach((key, value) { + _cannotModifiy(key); finalTags[key.index] = value; }); final bool resultEditAudio = await _channel.invokeMethod("editAudio", { "data": data, "tags": finalTags, + "searchInsideFolders": searchInsideFolders, }); return resultEditAudio; } @@ -246,11 +265,14 @@ class OnAudioEdit { /// The request status and folder permission will be saved as persistent but /// if user uninstall the app, this permission will be removed. Future editAudios( - List data, List> tags) async { + List data, + List> tags, + ) async { List> finalList = []; for (var it1 in tags) { Map finalTags = {}; it1.forEach((key, value) { + _cannotModifiy(key); finalTags[key.index] = value; }); finalList.add(finalTags); @@ -278,7 +300,7 @@ class OnAudioEdit { /// * [data] is used to find multiples audios data. /// * [openFilePicker] is used to define if folder picker will be open to user choose image. /// * [imagePath] is used to define image path, only necessary if [openFilePicker] is false. - /// * [format] is used to define image type: [JPG], [JPEG] or [JPEG]. + /// * [format] is used to define image type: [PNG] or [JPEG]. /// * [size] is used to define image quality. /// * [description] is used to define artwork description. /// @@ -291,12 +313,14 @@ class OnAudioEdit { /// * If [format] is null, will be set to [JPEG]. /// * If [size] is null, will be set to [24]. /// * If [description] is null, will be set to ["artwork"]. - Future editArtwork(String data, - [bool? openFilePicker, - String? imagePath, - ArtworkFormat? format, - int? size, - String? description]) async { + Future editArtwork( + String data, [ + bool? openFilePicker, + String? imagePath, + ArtworkFormat? format, + int? size, + String? description, + ]) async { assert( openFilePicker == false || imagePath == null, "Cannot change artwork image without image.\n" @@ -341,8 +365,10 @@ class OnAudioEdit { /// * This method only works on Android 9 or below (later i will add support android 10). /// * If return true delete works, else delete found a problem. Future deleteArtworks(List data) async { - final bool resultDeleteArts = - await _channel.invokeMethod("deleteArtworks", {"data": data}); + final bool resultDeleteArts = await _channel.invokeMethod( + "deleteArtworks", + {"data": data}, + ); return resultDeleteArts; } @@ -358,8 +384,10 @@ class OnAudioEdit { /// * This method only works on Android 9 or below (later i will add support android 10). /// * If return true delete works, else delete found a problem. Future deleteAudio(String data) async { - final bool resultDelete = - await _channel.invokeMethod("deleteAudio", {"data": data}); + final bool resultDelete = await _channel.invokeMethod( + "deleteAudio", + {"data": data}, + ); return resultDelete; } @@ -382,8 +410,9 @@ class OnAudioEdit { /// * This method only works on Android 10 or above. /// * If return true Complex Permission is Granted, else Complex Permission is Denied. Future complexPermissionStatus() async { - final bool resultStatusComplex = - await _channel.invokeMethod("complexPermissionStatus"); + final bool resultStatusComplex = await _channel.invokeMethod( + "complexPermissionStatus", + ); return resultStatusComplex; } @@ -395,8 +424,9 @@ class OnAudioEdit { /// * This method only works on Android 10 or above. /// * If return true, Complex Permission was called, else Complex Permission was't called. Future requestComplexPermission() async { - final bool resultRequestComplex = - await _channel.invokeMethod("requestComplexPermission"); + final bool resultRequestComplex = await _channel.invokeMethod( + "requestComplexPermission", + ); return resultRequestComplex; } @@ -408,15 +438,25 @@ class OnAudioEdit { /// * This method only works on Android 10 or above. /// * If return true, Complex Permission was reset, else Complex Permission was't reset. Future resetComplexPermission() async { - final bool resultReset = - await _channel.invokeMethod("resetComplexPermission"); + final bool resultReset = await _channel.invokeMethod( + "resetComplexPermission", + ); return resultReset; } - /// Used to open image folder to user select image and return this image path. - Future getImagePath() async { - final String resultImagePath = await _channel.invokeMethod("getImagePath"); - return resultImagePath; + /// Deprecated after [1.2.0]. + @Deprecated('Use [getImage] instead.') + Future getImagePath({ArtworkFormat? format, int? quality}) async { + return getImage(format: format, quality: quality); + } + + /// Used to open image folder to user select image and return this [ImageModel]. + Future getImage({ArtworkFormat? format, int? quality}) async { + final Map resultImage = await _channel.invokeMethod("getImagePath", { + "format": format ?? ArtworkFormat.JPEG.index, + "quality": quality ?? 100, + }); + return ImageModel(resultImage); } /// Used to return a converted value from file length. diff --git a/lib/details/types/artwork_type.dart b/lib/details/types/artwork_type.dart index 1249dc8..5de430e 100644 --- a/lib/details/types/artwork_type.dart +++ b/lib/details/types/artwork_type.dart @@ -4,9 +4,6 @@ part of on_audio_edit; /// Defines the type of image. enum ArtworkFormat { - /// - JPG, - /// Note: [JPEG] images give a better performance when call the method and give a "bad" image quality. JPEG, diff --git a/lib/details/types/tag_type.dart b/lib/details/types/tag_type.dart index 2269876..7de92a6 100644 --- a/lib/details/types/tag_type.dart +++ b/lib/details/types/tag_type.dart @@ -4,30 +4,60 @@ part of on_audio_edit; /// All the songs tags type. enum TagType { + /// + ACOUSTID_FINGERPRINT, + + /// + ACOUSTID_ID, + /// ALBUM, /// ALBUM_ARTIST, + /// + ALBUM_ARTIST_SORT, + + /// + ALBUM_SORT, + + /// + AMAZON_ID, + + /// + ARRANGER, + /// ARTIST, + /// + ARTIST_SORT, + /// ARTISTS, /// - BEATS_PER_MINUTE, + BARCODE, /// - BITRATE, + BPM, /// - CHANNELS, + CATALOG_NO, + + /// + COMMENT, /// COMPOSER, + /// + COMPOSER_SORT, + + /// + CONDUCTOR, + /// COUNTRY, @@ -35,17 +65,53 @@ enum TagType { COVER_ART, /// - FIRST_ARTWORK, + CUSTOM1, /// - FORMAT, + CUSTOM2, + + /// + CUSTOM3, + + /// + CUSTOM4, + + /// + CUSTOM5, + + /// + DISC_NO, + + /// + DISC_SUBTITLE, + + /// + DISC_TOTAL, + + /// + DJMIXER, + + /// + ENCODER, + + /// + ENGINEER, + + /// + FBPM, /// GENRE, + /// + GROUPING, + /// ISRC, + /// + IS_COMPILATION, + /// KEY, @@ -53,11 +119,62 @@ enum TagType { LANGUAGE, /// - LENGTH, + LYRICIST, /// LYRICS, + /// + MEDIA, + + /// + MIXER, + + /// + MOOD, + + /// + MUSICBRAINZ_ARTISTID, + + /// + MUSICBRAINZ_DISC_ID, + + /// + MUSICBRAINZ_ORIGINAL_RELEASE_ID, + + /// + MUSICBRAINZ_RELEASEARTISTID, + + /// + MUSICBRAINZ_RELEASEID, + + /// + MUSICBRAINZ_RELEASE_COUNTRY, + + /// + MUSICBRAINZ_RELEASE_GROUP_ID, + + /// + MUSICBRAINZ_RELEASE_STATUS, + + /// + MUSICBRAINZ_RELEASE_TRACK_ID, + + /// + MUSICBRAINZ_RELEASE_TYPE, + + /// + MUSICBRAINZ_TRACK_ID, + + /// + MUSICBRAINZ_WORK_ID, + + /// + MUSICIP_ID, + + /// + OCCASION, + /// ORIGINAL_ALBUM, @@ -71,10 +188,10 @@ enum TagType { ORIGINAL_YEAR, /// - PRODUCER, + QUALITY, /// - QUALITY, + PRODUCER, /// RATING, @@ -83,7 +200,10 @@ enum TagType { RECORD_LABEL, /// - SAMPLE_RATE, + REMIXER, + + /// + SCRIPT, /// SUBTITLE, @@ -97,73 +217,59 @@ enum TagType { /// TITLE, + /// + TITLE_SORT, + /// TRACK, /// - TYPE, + TRACK_TOTAL, + + /// + URL_DISCOGS_ARTIST_SITE, + + /// + URL_DISCOGS_RELEASE_SITE, + + /// + URL_LYRICS_SITE, + + /// + URL_OFFICIAL_ARTIST_SITE, + + /// + URL_OFFICIAL_RELEASE_SITE, + + /// + URL_WIKIPEDIA_ARTIST_SITE, + + /// + URL_WIKIPEDIA_RELEASE_SITE, /// YEAR, -} -// All Tags -// { -// ACOUSTID_FINGERPRINT, -// ACOUSTID_ID, -// ALBUM, -// ALBUM_ARTIST_SORT, -// ALBUM_SORT, -// AMAZON_ID, -// ARRANGER, -// ARTIST_SORT, -// BARCODE, -// CATALOG_NO, -// COMMENT, -// COMPOSER_SORT, -// CONDUCTOR, -// COVER_ART, -// CUSTOM1, -// CUSTOM2, -// CUSTOM3, -// CUSTOM4, -// CUSTOM5, -// DISC_NO, -// DISC_SUBTITLE, -// DISC_TOTAL, -// DJMIXER, -// ENCODER, -// ENGINEER, -// FBPM, -// GROUPING, -// IS_COMPILATION, -// LYRICIST, -// MEDIA, -// MIXER, -// MOOD, -// MUSICBRAINZ_ARTISTID, -// MUSICBRAINZ_DISC_ID, -// MUSICBRAINZ_ORIGINAL_RELEASE_ID, -// MUSICBRAINZ_RELEASEARTISTID, -// MUSICBRAINZ_RELEASEID, -// MUSICBRAINZ_RELEASE_COUNTRY, -// MUSICBRAINZ_RELEASE_GROUP_ID, -// MUSICBRAINZ_RELEASE_STATUS, -// MUSICBRAINZ_RELEASE_TRACK_ID, -// MUSICBRAINZ_RELEASE_TYPE, -// MUSICBRAINZ_TRACK_ID, -// MUSICBRAINZ_WORK_ID, -// MUSICIP_ID, -// OCCASION, -// REMIXER, -// SCRIPT, -// TITLE_SORT, -// TRACK_TOTAL, -// URL_DISCOGS_ARTIST_SITE, -// URL_DISCOGS_RELEASE_SITE, -// URL_LYRICS_SITE, -// URL_OFFICIAL_ARTIST_SITE, -// URL_OFFICIAL_RELEASE_SITE, -// URL_WIKIPEDIA_ARTIST_SITE, -// URL_WIKIPEDIA_RELEASE_SITE, -// } + // Non editable tags + + /// This tag cannot be modified! + BITRATE, + + /// This tag cannot be modified! + CHANNELS, + + /// This tag cannot be modified! + FIRST_ARTWORK, + + /// This tag cannot be modified! + FORMAT, + + /// This tag cannot be modified! + TRACK_LENGTH, + + /// This tag cannot be modified! + SAMPLE_RATE, + + /// This tag cannot be modified! + ENCODING_TYPE, +} diff --git a/lib/on_audio_edit.dart b/lib/on_audio_edit.dart index 4c2f9db..3706ef9 100644 --- a/lib/on_audio_edit.dart +++ b/lib/on_audio_edit.dart @@ -16,6 +16,8 @@ library on_audio_edit; //Dart/Flutter import 'dart:async'; +import 'dart:developer'; +import 'dart:typed_data'; import 'package:flutter/services.dart'; // Controller @@ -28,3 +30,4 @@ part 'details/types/artwork_type.dart'; // Models part 'details/models/audios_model.dart'; part 'details/models/audio_model.dart'; +part 'details/models/image_model.dart'; From e877222430dfd72e575e03f234785e3ca69537b1 Mon Sep 17 00:00:00 2001 From: Lucas Pinheiro Date: Fri, 22 Oct 2021 15:12:42 -0300 Subject: [PATCH 5/5] Release of `1.2.0` --- CHANGELOG.md | 30 ++++++++++++++++++ DEPRECATED.md | 3 ++ README.md | 77 +++++++++++++++++++++++++--------------------- README.pt-BR.md | 81 +++++++++++++++++++++++++++---------------------- 4 files changed, 121 insertions(+), 70 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a26a045..e87cc58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,33 @@ +## [1.2.0] - [10.22.2021] +### Features +- **[Added]** option to search inside all folders when using `[editAudio]` on Android 10 or above. - [#5](https://github.com/LucasPJS/on_audio_query/issues/5) +- **[Added]** `ALL` possible tag type. +- **[Added]** `ALL` possible getter to `[AudioModel]`. +- **[Created]** `[ImageModel]`. + +### Fixes +- **[Fixed]** no ALBUM tag in TagType. - [#2](https://github.com/LucasPJS/on_audio_query/issues/2) +- **[Fixed]** bug with permission methods. - [#4](https://github.com/LucasPJS/on_audio_query/issues/4) + +### Documentation +- Updated `README` documentation. + +### Changes +#### Dart +- Changed `[readAudio]`, `[readAllAudio]` and `[readSpecificsAudioTags]` return type from `Map` to `AudioModel`. + +### ⚠ Important Changes +#### Dart +- Now `[getImage]` a `[ImageModel]`. - [#1](https://github.com/LucasPJS/on_audio_query/issues/1) +- Now `[readAudio]`, `[readAllAudio ]` and `[readSpecificsAudioTags]` return type from `Map` to `AudioModel`. + +#### @Deprecated +- `[readAllAudio]`. + - Use `[readAudio]` instead. +- `[getImagePath]`. + - Use `[getImage]` instead. +- `[id]` from `[AudioModel]`. + ## [1.1.0] - [10.20.2021] ### Features #### Dart diff --git a/DEPRECATED.md b/DEPRECATED.md index 9adc836..784666b 100644 --- a/DEPRECATED.md +++ b/DEPRECATED.md @@ -2,6 +2,9 @@ ### Deprecated - `[readAllAudio]`. - Use `[readAudio]` instead. +- `[getImagePath]`. + - Use `[getImage]` instead. +- `[id]` from `[AudioModel]`. ## [1.1.0] - [10.20.2021] -> [X.X.X] - [XX.XX.XXXX] ### Deprecated diff --git a/README.md b/README.md index c2d1513..699ce72 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ NOTE: Feel free to help with readme translations Add the following code to your `pubspec.yaml`: ```yaml dependencies: - on_audio_edit: ^1.1.0 + on_audio_edit: ^1.2.0 ``` #### Request Permission: @@ -80,20 +80,36 @@ OnAudioEdit() // The main method to start using the plugin. ``` All types of methods on this plugin: +### Read methods + | Methods | Parameters | Return | |--------------|-----------------|-----------------| -| [`readAudio`](#readaudio) | `(String)` | `Map` |
-| [`readAllAudio`](#readallaudio) | `(String)` | `Map` |
-| [`readAudios`](#readaudios) | `(List)` | `List` |
+| [`readAudio`](#readaudio) | `(String)` | `AudioModel` |
+| [`readAudios`](#readaudios) | `(List)` | `List` |
| [`readSingleAudioTag`](#readsingleaudiotag) | `(String, TagsType)` | `String` |
-| [`readSpecificsAudioTags`](#readspecificsaudiotags) | `(String, List)` | `Map` |
+| [`readSpecificsAudioTags`](#readspecificsaudiotags) | `(String, List)` | `AudioModel` |
+ +### Edit methods + +| Methods | Parameters | Return | +|--------------|-----------------|-----------------| | [`editAudio`](#editaudio) | `(String, Map)` | `bool` |
| [`editAudios`](#editaudios) | `(List, List>)` | `bool` |
| [`editArtwork`](#editartwork) | `(String, bool, String, ArtworkFormat, int, String)` | `bool` |
+ +### Delete methods + +| Methods | Parameters | Return | +|--------------|-----------------|-----------------| | [`deleteArtwork`]() | **[W]**`(String)` | `bool` |
| [`deleteArtworks`]() | **[W]**`(List)` | `bool` |
| [`deleteAudio`]() | **[W]**`(String)` | `bool` |
-| [`getImagePath`]() | | `String` |
+ +### Permission/Image methods + +| Methods | Parameters | Return | +|--------------|-----------------|-----------------| +| [`getImage`]() | `ArtworkFormat, Quality` | `ImageModel` |
| [`permissionsStatus`]() | | `bool` |
| [`resetComplexPermission`]() | **[Q]** | `bool` |
| [`requestComplexPermission`]() | **[Q]** | `bool` |
@@ -111,47 +127,40 @@ All types of methods on this plugin: | `ARTISTS` | `ORIGINAL_YEAR` | `LANGUAGE` | `TEMPO` | `CHANNELS` |
| `BEATS_PER_MINUTE` | `PRODUCER` | `KEY` | `TAGS` | `COVER_ART` |
| `COMPOSER` | `QUALITY` | `ISRC` | `SUBTITLE` | `TYPE` |
-| `COUNTRY` | `RATING` | `FIRST_ARTWORK` | `LENGTH` | +| `COUNTRY` | `RATING` | `FIRST_ARTWORK` | `LENGTH` | [`More`](https://github.com/LucasPJS/on_audio_edit/blob/main/lib/details/types/tag_type.dart) |
| `GENRE` | `RECORD_LABEL` | `YEAR` | `BITRATE` |
-**Using [readAllAudio](#readallaudio) you can get more infos about audio. See all list in: [AllTags](https://github.com/LucasPJS/on_audio_edit/blob/main/lib/details/types/tag_type.dart)** - ## Examples: -#### readAudio +#### OnAudioEdit ```dart - // data: "/storage/1E65-6GH3/SomeMusic.mp3" or "/storage/someFolder/SomeMusic.mp3" - Map song = await OnAudioEdit().readAudio(data); - var songTitle = song["TITLE"]; - var songArtist = song["ARTIST"]; + final OnAudioEdit _audioEdit = OnAudioEdit(); ``` -#### readAllAudio -This method read all possible info from a audio. See all list in: [AllTags](https://github.com/LucasPJS/on_audio_edit/blob/main/lib/details/types/tag_type.dart) +#### readAudio ```dart - Map song = await OnAudioEdit().readAllAudio(data); - var songInfo1 = song["MIXER"]; - var songInfo2 = song["MUSICBRAINZ_ORIGINAL_RELEASE_ID"]; - var songInfo3 = song["AMAZON_ID"]; - var songInfo4 = song["CONDUCTOR"]; + // data: "/storage/1E65-6GH3/SomeMusic.mp3" or "/storage/someFolder/SomeMusic.mp3" + AudioModel song = await _audioEdit.readAudio(data); + String songTitle = song.title; + String songArtist = song.artist ?? ''; ``` #### readAudios ```dart List allData = [data0, data1, data2]; - List song = await OnAudioEdit().readAudios(allData); + List song = await _audioEdit.readAudios(allData); ... - var songTitle1 = song[1].title; - var songTitle2 = song[2].title; - var songTitle3 = song[3].title; + String songTitle1 = song[0].title; + String songTitle2 = song[1].title; + String songTitle3 = song[2].title; ``` #### readSingleAudioTag ```dart - String title = await OnAudioEdit().readSingleAudioTag(data, TagsType.TITLE); + String title = await _audioEdit.readSingleAudioTag(data, TagsType.TITLE); print(title); // Ex: Heavy, California ... - String artist = await OnAudioEdit().readSingleAudioTag(data, TagsType.ARTIST); + String artist = await _audioEdit.readSingleAudioTag(data, TagsType.ARTIST); print(artist); // Ex: Jungle ``` @@ -161,10 +170,10 @@ This method read all possible info from a audio. See all list in: [AllTags](http TagsType.TITLE, TagsType.ARTIST ]; - var songSpecifics = await OnAudioEdit().readSpecificsAudioTags(data, tags); + AudioModel songSpecifics = await _audioEdit.readSpecificsAudioTags(data, tags); ... - var songTitle = songSpecifics["TITLE"]; - var songArtist = songSpecifics["ARTIST"]; + String songTitle = songSpecifics.title; + String songArtist = songSpecifics ?? ''; ``` #### editAudio @@ -173,7 +182,7 @@ This method read all possible info from a audio. See all list in: [AllTags](http TagsType.TITLE: "New Title", TagsType.ARTIST: "New Artist" }; - bool song = await OnAudioEdit().editAudio(data, tags); + bool song = await _audioEdit.editAudio(data, tags); print(song); //True or False ``` @@ -193,16 +202,16 @@ This method read all possible info from a audio. See all list in: [AllTags](http data.add(song1); data.add(song2); data.add(song3); - bool result = await OnAudioEdit().editAudios(data, tags); + bool result = await _audioEdit.editAudios(data, tags); print(result); //True or False ``` -#### editAudio +#### editArtwork ⚠ **Note: If openFilePicker is false, imagePath can't be null.** ```dart // Parameters: openFilePicker, imagePath, format, size, description // DEFAULT: true, null, ArtworkFormat.JPEG, 24, "artwork" - bool song = await OnAudioEdit().editArtwork(data); + bool song = await _audioEdit.editArtwork(data); print(song); //True or False ``` diff --git a/README.pt-BR.md b/README.pt-BR.md index b83e5a6..3bef9dc 100644 --- a/README.pt-BR.md +++ b/README.pt-BR.md @@ -34,7 +34,7 @@ NOTE: Fique à vontade para ajudar nas traduções Adicione o seguinte codigo para seu `pubspec.yaml`: ```yaml dependencies: - on_audio_edit: ^1.1.0 + on_audio_edit: ^1.2.0 ``` #### Solicitar Permissões: @@ -80,20 +80,36 @@ OnAudioEdit() // O comando principal para usar o plugin. ``` Todos os tipos de métodos nesse plugin: +### Read methods + | Methods | Parameters | Return | |--------------|-----------------|-----------------| -| [`readAudio`](#readaudio) | `(String)` | `Map` |
-| [`readAllAudio`](#readallaudio) | `(String)` | `Map` |
-| [`readAudios`](#readaudios) | `(List)` | `List` |
+| [`readAudio`](#readaudio) | `(String)` | `AudioModel` |
+| [`readAudios`](#readaudios) | `(List)` | `List` |
| [`readSingleAudioTag`](#readsingleaudiotag) | `(String, TagsType)` | `String` |
-| [`readSpecificsAudioTags`](#readspecificsaudiotags) | `(String, List)` | `Map` |
+| [`readSpecificsAudioTags`](#readspecificsaudiotags) | `(String, List)` | `AudioModel` |
+ +### Edit methods + +| Methods | Parameters | Return | +|--------------|-----------------|-----------------| | [`editAudio`](#editaudio) | `(String, Map)` | `bool` |
| [`editAudios`](#editaudios) | `(List, List>)` | `bool` |
| [`editArtwork`](#editartwork) | `(String, bool, String, ArtworkFormat, int, String)` | `bool` |
+ +### Delete methods + +| Methods | Parameters | Return | +|--------------|-----------------|-----------------| | [`deleteArtwork`]() | **[W]**`(String)` | `bool` |
| [`deleteArtworks`]() | **[W]**`(List)` | `bool` |
| [`deleteAudio`]() | **[W]**`(String)` | `bool` |
-| [`getImagePath`]() | | `String` |
+ +### Permission/Image methods + +| Methods | Parameters | Return | +|--------------|-----------------|-----------------| +| [`getImage`]() | `ArtworkFormat, Quality` | `ImageModel` |
| [`permissionsStatus`]() | | `bool` |
| [`resetComplexPermission`]() | **[Q]** | `bool` |
| [`requestComplexPermission`]() | **[Q]** | `bool` |
@@ -111,47 +127,40 @@ Todos os tipos de métodos nesse plugin: | `ARTISTS` | `ORIGINAL_YEAR` | `LANGUAGE` | `TEMPO` | `CHANNELS` |
| `BEATS_PER_MINUTE` | `PRODUCER` | `KEY` | `TAGS` | `COVER_ART` |
| `COMPOSER` | `QUALITY` | `ISRC` | `SUBTITLE` | `TYPE` |
-| `COUNTRY` | `RATING` | `FIRST_ARTWORK` | `LENGTH` | +| `COUNTRY` | `RATING` | `FIRST_ARTWORK` | `LENGTH` | [`More`](https://github.com/LucasPJS/on_audio_edit/blob/main/lib/details/types/tag_type.dart) |
| `GENRE` | `RECORD_LABEL` | `YEAR` | `BITRATE` |
-**Usando [readAllAudio](#readallaudio) você pode ver mais informações sobre o/os audio/audios. Veja toda a lista em: [AllTags](https://github.com/LucasPJS/on_audio_edit/blob/main/lib/details/types/tag_type.dart)** - ## Exemplos: -#### readAudio +#### OnAudioEdit ```dart - // data: "/storage/1E65-6GH3/SomeMusic.mp3" or "/storage/someFolder/SomeMusic.mp3" - Map song = await OnAudioEdit().readAudio(data); - var songTitle = song["TITLE"]; - var songArtist = song["ARTIST"]; + final OnAudioEdit _audioEdit = OnAudioEdit(); ``` -#### readAllAudio -Esse método ler toda informação possível de um audio. Veja toda a lista em: [AllTags](https://github.com/LucasPJS/on_audio_edit/blob/main/lib/details/types/tag_type.dart) +#### readAudio ```dart - Map song = await OnAudioEdit().readAllAudio(data); - var songInfo1 = song["MIXER"]; - var songInfo2 = song["MUSICBRAINZ_ORIGINAL_RELEASE_ID"]; - var songInfo3 = song["AMAZON_ID"]; - var songInfo4 = song["CONDUCTOR"]; + // data: "/storage/1E65-6GH3/SomeMusic.mp3" or "/storage/someFolder/SomeMusic.mp3" + AudioModel song = await _audioEdit.readAudio(data); + String songTitle = song.title; + String songArtist = song.artist ?? ''; ``` #### readAudios ```dart List allData = [data0, data1, data2]; - List song = await OnAudioEdit().readAudios(allData); + List song = await _audioEdit.readAudios(allData); ... - var songTitle1 = song[1].title; - var songTitle2 = song[2].title; - var songTitle3 = song[3].title; + String songTitle1 = song[0].title; + String songTitle2 = song[1].title; + String songTitle3 = song[2].title; ``` #### readSingleAudioTag ```dart - String title = await OnAudioEdit().readSingleAudioTag(data, TagsType.TITLE); + String title = await _audioEdit.readSingleAudioTag(data, TagsType.TITLE); print(title); // Ex: Heavy, California ... - String artist = await OnAudioEdit().readSingleAudioTag(data, TagsType.ARTIST); + String artist = await _audioEdit.readSingleAudioTag(data, TagsType.ARTIST); print(artist); // Ex: Jungle ``` @@ -161,10 +170,10 @@ Esse método ler toda informação possível de um audio. Veja toda a lista em: TagsType.TITLE, TagsType.ARTIST ]; - var songSpecifics = await OnAudioEdit().readSpecificsAudioTags(data, tags); + AudioModel songSpecifics = await _audioEdit.readSpecificsAudioTags(data, tags); ... - var songTitle = songSpecifics["TITLE"]; - var songArtist = songSpecifics["ARTIST"]; + String songTitle = songSpecifics.title; + String songArtist = songSpecifics ?? ''; ``` #### editAudio @@ -173,12 +182,12 @@ Esse método ler toda informação possível de um audio. Veja toda a lista em: TagsType.TITLE: "New Title", TagsType.ARTIST: "New Artist" }; - bool song = await OnAudioEdit().editAudio(data, tags); + bool song = await _audioEdit.editAudio(data, tags); print(song); //True or False ``` #### editAudios -⚠ **Note: Esse método não está funcionando no Android 10 ou superior. Use: [editAudio](#editaudio)** +⚠ **Note: This method isn't implemented on Android 10 or above. Instead use: [editAudio](#editaudio)** ```dart // Tags List<> tags = []; @@ -193,16 +202,16 @@ Esse método ler toda informação possível de um audio. Veja toda a lista em: data.add(song1); data.add(song2); data.add(song3); - bool result = await OnAudioEdit().editAudios(data, tags); + bool result = await _audioEdit.editAudios(data, tags); print(result); //True or False ``` -#### editAudio -⚠ **Note: Se openFilePicker é falso, imagePath não pode ser nulo.** +#### editArtwork +⚠ **Note: If openFilePicker is false, imagePath can't be null.** ```dart // Parameters: openFilePicker, imagePath, format, size, description // DEFAULT: true, null, ArtworkFormat.JPEG, 24, "artwork" - bool song = await OnAudioEdit().editArtwork(data); + bool song = await _audioEdit.editArtwork(data); print(song); //True or False ```